admins: school statistic module

dev_hs
p31729568 6 years ago
parent bba5f20ffe
commit dc8f291d9d

@ -6,13 +6,13 @@
//= require bootstrap-sprockets
//= require jquery.validate.min
//= require additional-methods.min
//= require jquery-validate-message-zh
//= require bootstrap-notify
//= require jquery.cookie.min
//= require select2
//= require select2-i18n.zh-CN
//= require jquery.cxselect
//= require bootstrap-datepicker
//= require_tree ./i18n
//= require_tree ./admins
// ******** select2 global config ********

@ -0,0 +1,12 @@
$(document).on('turbolinks:load', function(){
if ($('body.admins-daily-school-statistics-index-page').length > 0) {
$('.export-action').on('click', function(){
var form = $(".daily-school-statistic-list-form .search-form")
var exportLink = $(this);
var keyword = form.find("input[name='keyword']").val();
var url = exportLink.data("url").split('?')[0] + "?keyword=" + keyword;
window.open(url);
});
}
})

@ -0,0 +1,135 @@
$(document).on('turbolinks:load', function(){
if ($('body.admins-school-statistics-index-page').length > 0) {
var searchForm = $(".school-statistic-list-form .search-form");
var growFormUrl = searchForm.data('grow-form-url');
var contrastFormUrl = searchForm.data('contrast-form-url');
var dataTypeInput = searchForm.find("input[name='data_type']");
var keywordInput = searchForm.find("input[name='keyword']");
var contrastBtn = searchForm.find(".contrast-btn");
var growBtn = searchForm.find(".grow-btn");
var contrastDateContainer = searchForm.find('.contrast-date-container');
var growDateContainer = searchForm.find('.grow-date-container');
// 数据对比日期输入框
var beginDateInput = searchForm.find("input[name='begin_date']");
var endDateInput = searchForm.find("input[name='end_date']");
var otherBeginDateInput = searchForm.find("input[name='other_begin_date']");
var otherEndDateInput = searchForm.find("input[name='other_end_date']");
// 新增数据日期输入框
var growBeginDateInput = searchForm.find("input[name='grow_begin_date']");
var growEndDateInput = searchForm.find("input[name='grow_end_date']");
// 数据展示切换: 数据对比、新增数据
searchForm.on('click', ".contrast-btn", function(){
if(contrastBtn.hasClass("active")) { return }
changeDataType("contrast");
submitForm();
});
searchForm.on('click', ".grow-btn", function(){
if(growBtn.hasClass("active")) { return }
changeDataType("grow");
submitForm();
});
// 搜索按钮
searchForm.on('click', ".search-btn", function(){
console.log('submit');
submitForm();
});
$('.school-statistic-list-container').on('change', '.contrast-column-select', function() {
searchForm.find("input[name='contrast_column']").val($('.contrast-column-select').val());
submitForm();
});
var submitForm = function(){
if(!validateFrom()) { return }
var form = searchForm;
var url = dataTypeInput.val() == "contrast" ? contrastFormUrl : growFormUrl;
$.ajax({
url: url,
data: form.serialize(),
dataType: "script"
})
};
var validateFrom = function(){
if (dataTypeInput.val() != "contrast") { return true; }
// 全部为空时,需要展示空数据页
if (beginDateInput.val() == "" && endDateInput.val() == "" &&
otherBeginDateInput.val() == "" && otherBeginDateInput.val() == "") {
return true;
}
if (beginDateInput.val() != "" && endDateInput.val() != "" &&
otherBeginDateInput.val() != "" && otherBeginDateInput.val() != "") {
return true;
}
return false;
};
var changeDataType = function(dataType){
if (dataTypeInput.val() == dataType) { return }
if (dataType == "contrast") {
contrastBtn.addClass("active");
growBtn.removeClass("active");
dataTypeInput.val('contrast');
growDateContainer.hide();
contrastDateContainer.show();
clearGrowDateInput();
} else {
contrastBtn.removeClass("active");
growBtn.addClass("active");
dataTypeInput.val('grow');
growDateContainer.show();
contrastDateContainer.hide();
clearContrastDateInput();
}
};
var clearGrowDateInput = function() {
searchForm.find("input[name='grow_begin_date']").val('');
searchForm.find("input[name='grow_end_date']").val('');
searchForm.find("input[name='grow_date_input']").val('');
};
var clearContrastDateInput = function(){
searchForm.find("input[name='begin_date']").val('');
searchForm.find("input[name='end_date']").val('');
searchForm.find("input[name='other_begin_date']").val('');
searchForm.find("input[name='other_end_date']").val('');
searchForm.find("input[name='date_input']").val('');
searchForm.find("input[name='other_date_input']").val('');
};
var baseOptions = {
autoclose: true,
language: 'zh-CN',
format: 'yyyy-mm-dd',
startDate: '2017-04-01',
endDate: '-1d'
}
var defineDateRangeSelect = function(element){
var options = $.extend({inputs: $(element).find('.start-date, .end-date')}, baseOptions);
$(element).datepicker(options);
$(element).find('.start-date').datepicker().on('changeDate', function(e){
$(element).find('.end-date').datepicker('setStartDate', e.date);
})
};
defineDateRangeSelect('.grow-date-input-daterange');
defineDateRangeSelect('.date-input-daterange');
defineDateRangeSelect('.other-date-input-daterange');
}
})

@ -0,0 +1,16 @@
$(document).on('turbolinks:load', function(){
$('#sidebarCollapse').on('click', function () {
$(this).toggleClass('active');
$('#sidebar').toggleClass('active');
$.cookie('admin_sidebar_collapse', $(this).hasClass('active'), {path: '/admins'});
});
var sidebarController = $('#sidebar').data('current-controller');
if (sidebarController.length > 0) {
$('#sidebar a.active').removeClass('active');
$('#sidebar ul.collapse.show').removeClass('show');
var activeLi = $('#sidebar a[data-controller="' + sidebarController + '"]');
activeLi.addClass('active');
activeLi.parent().parent('ul.collapse').addClass('show');
}
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",monthsTitle:"选择月份",clear:"清除",format:"yyyy-mm-dd",titleFormat:"yyyy年mm月",weekStart:1}}(jQuery);

@ -3,6 +3,8 @@
@import "font-awesome";
@import "select2.min";
@import "select2-bootstrap4.min";
@import "bootstrap-datepicker";
@import "bootstrap-datepicker.standalone";
@import "admins/*";

@ -40,6 +40,7 @@
}
& > td {
text-align: center;
height: 300px;
}
}
@ -72,14 +73,32 @@
/* 搜索表单 */
.search-form-container {
display: flex;
margin-bottom: 20px;
.search-form {
flex: 1;
* { font-size: 14px; }
select, input {
margin-right: 10px;
font-size: 14px;
}
}
}
.global-error {
color: grey;
min-height: 300px;
&-code {
font-size: 80px;
}
&-text {
font-size: 24px;
}
}
}

@ -0,0 +1,5 @@
.admins-daily-school-statistics-index-page {
.daily-school-statistic-list-container {
text-align: center;
}
}

@ -0,0 +1,43 @@
.admins-school-statistics-index-page {
.school-statistic-list-form {
.time-select {
flex: 1;
}
.type-box {
.btn { margin: 0 5px; }
}
.search-input {
width: 220px;
}
.contrast-date-container {
display: flex;
align-items: center;
}
}
.school-statistic-list-container {
.contrast-column-select {
position: absolute;
right: 30px;
top: 15px;
width: 130px;
}
.relative {
position: relative;
}
.right-border::after {
position: absolute;
top: 10px;
right: 0;
content: '';
width: 0;
height: 20px;
border-right: 1px solid #000;
}
}
}

@ -0,0 +1,477 @@
/*!
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
.datepicker {
padding: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
direction: ltr;
}
.datepicker-inline {
width: 220px;
}
.datepicker-rtl {
direction: rtl;
}
.datepicker-rtl.dropdown-menu {
left: auto;
}
.datepicker-rtl table tr td span {
float: right;
}
.datepicker-dropdown {
top: 0;
left: 0;
}
.datepicker-dropdown:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #999;
border-top: 0;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
.datepicker-dropdown:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-top: 0;
position: absolute;
}
.datepicker-dropdown.datepicker-orient-left:before {
left: 6px;
}
.datepicker-dropdown.datepicker-orient-left:after {
left: 7px;
}
.datepicker-dropdown.datepicker-orient-right:before {
right: 6px;
}
.datepicker-dropdown.datepicker-orient-right:after {
right: 7px;
}
.datepicker-dropdown.datepicker-orient-bottom:before {
top: -7px;
}
.datepicker-dropdown.datepicker-orient-bottom:after {
top: -6px;
}
.datepicker-dropdown.datepicker-orient-top:before {
bottom: -7px;
border-bottom: 0;
border-top: 7px solid #999;
}
.datepicker-dropdown.datepicker-orient-top:after {
bottom: -6px;
border-bottom: 0;
border-top: 6px solid #fff;
}
.datepicker table {
margin: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.datepicker td,
.datepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
}
.table-striped .datepicker table tr td,
.table-striped .datepicker table tr th {
background-color: transparent;
}
.datepicker table tr td.day:hover,
.datepicker table tr td.day.focused {
background: #eee;
cursor: pointer;
}
.datepicker table tr td.old,
.datepicker table tr td.new {
color: #999;
}
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover {
background: none;
color: #999;
cursor: default;
}
.datepicker table tr td.highlighted {
background: #d9edf7;
border-radius: 0;
}
.datepicker table tr td.today,
.datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:hover {
background-color: #fde19a;
background-image: -moz-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -ms-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
background-image: -webkit-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -o-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: linear-gradient(to bottom, #fdd49a, #fdf59a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #000;
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today.disabled:hover:hover,
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today:hover.disabled,
.datepicker table tr td.today.disabled.disabled,
.datepicker table tr td.today.disabled:hover.disabled,
.datepicker table tr td.today[disabled],
.datepicker table tr td.today:hover[disabled],
.datepicker table tr td.today.disabled[disabled],
.datepicker table tr td.today.disabled:hover[disabled] {
background-color: #fdf59a;
}
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active {
background-color: #fbf069 \9;
}
.datepicker table tr td.today:hover:hover {
color: #000;
}
.datepicker table tr td.today.active:hover {
color: #fff;
}
.datepicker table tr td.range,
.datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:hover {
background: #eee;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today,
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:hover {
background-color: #f3d17a;
background-image: -moz-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -ms-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
background-image: -webkit-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -o-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: linear-gradient(to bottom, #f3c17a, #f3e97a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
border-color: #f3e97a #f3e97a #edde34;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today:hover:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today:hover.disabled,
.datepicker table tr td.range.today.disabled.disabled,
.datepicker table tr td.range.today.disabled:hover.disabled,
.datepicker table tr td.range.today[disabled],
.datepicker table tr td.range.today:hover[disabled],
.datepicker table tr td.range.today.disabled[disabled],
.datepicker table tr td.range.today.disabled:hover[disabled] {
background-color: #f3e97a;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active {
background-color: #efe24b \9;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected.disabled:hover {
background-color: #9e9e9e;
background-image: -moz-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -ms-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
background-image: -webkit-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -o-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: linear-gradient(to bottom, #b3b3b3, #808080);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
border-color: #808080 #808080 #595959;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected:hover:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected:hover.disabled,
.datepicker table tr td.selected.disabled.disabled,
.datepicker table tr td.selected.disabled:hover.disabled,
.datepicker table tr td.selected[disabled],
.datepicker table tr td.selected:hover[disabled],
.datepicker table tr td.selected.disabled[disabled],
.datepicker table tr td.selected.disabled:hover[disabled] {
background-color: #808080;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active {
background-color: #666666 \9;
}
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
background-image: linear-gradient(to bottom, #08c, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.active:hover,
.datepicker table tr td.active:hover:hover,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.disabled:hover:hover,
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active:hover.disabled,
.datepicker table tr td.active.disabled.disabled,
.datepicker table tr td.active.disabled:hover.disabled,
.datepicker table tr td.active[disabled],
.datepicker table tr td.active:hover[disabled],
.datepicker table tr td.active.disabled[disabled],
.datepicker table tr td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datepicker table tr td span:hover,
.datepicker table tr td span.focused {
background: #eee;
}
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
background: none;
color: #999;
cursor: default;
}
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
background-image: linear-gradient(to bottom, #08c, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active:hover:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active:hover.disabled,
.datepicker table tr td span.active.disabled.disabled,
.datepicker table tr td span.active.disabled:hover.disabled,
.datepicker table tr td span.active[disabled],
.datepicker table tr td span.active:hover[disabled],
.datepicker table tr td span.active.disabled[disabled],
.datepicker table tr td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span.old,
.datepicker table tr td span.new {
color: #999;
}
.datepicker .datepicker-switch {
width: 145px;
}
.datepicker .datepicker-switch,
.datepicker .prev,
.datepicker .next,
.datepicker tfoot tr th {
cursor: pointer;
}
.datepicker .datepicker-switch:hover,
.datepicker .prev:hover,
.datepicker .next:hover,
.datepicker tfoot tr th:hover {
background: #eee;
}
.datepicker .prev.disabled,
.datepicker .next.disabled {
visibility: hidden;
}
.datepicker .cw {
font-size: 10px;
width: 12px;
padding: 0 2px 0 5px;
vertical-align: middle;
}
.input-append.date .add-on,
.input-prepend.date .add-on {
cursor: pointer;
}
.input-append.date .add-on i,
.input-prepend.date .add-on i {
margin-top: 3px;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
-webkit-border-radius: 3px 0 0 3px;
-moz-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
-webkit-border-radius: 0 3px 3px 0;
-moz-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0;
}
.input-daterange .add-on {
display: inline-block;
width: auto;
min-width: 16px;
height: 18px;
padding: 4px 5px;
font-weight: normal;
line-height: 18px;
text-align: center;
text-shadow: 0 1px 0 #fff;
vertical-align: middle;
background-color: #eee;
border: 1px solid #ccc;
margin-left: -5px;
margin-right: -5px;
}
/*# sourceMappingURL=bootstrap-datepicker.css.map */

@ -0,0 +1,510 @@
/*!
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
.datepicker {
padding: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
direction: ltr;
}
.datepicker-inline {
width: 220px;
}
.datepicker-rtl {
direction: rtl;
}
.datepicker-rtl.dropdown-menu {
left: auto;
}
.datepicker-rtl table tr td span {
float: right;
}
.datepicker-dropdown {
top: 0;
left: 0;
}
.datepicker-dropdown:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #999;
border-top: 0;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
.datepicker-dropdown:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-top: 0;
position: absolute;
}
.datepicker-dropdown.datepicker-orient-left:before {
left: 6px;
}
.datepicker-dropdown.datepicker-orient-left:after {
left: 7px;
}
.datepicker-dropdown.datepicker-orient-right:before {
right: 6px;
}
.datepicker-dropdown.datepicker-orient-right:after {
right: 7px;
}
.datepicker-dropdown.datepicker-orient-bottom:before {
top: -7px;
}
.datepicker-dropdown.datepicker-orient-bottom:after {
top: -6px;
}
.datepicker-dropdown.datepicker-orient-top:before {
bottom: -7px;
border-bottom: 0;
border-top: 7px solid #999;
}
.datepicker-dropdown.datepicker-orient-top:after {
bottom: -6px;
border-bottom: 0;
border-top: 6px solid #fff;
}
.datepicker table {
margin: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.datepicker td,
.datepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
}
.table-striped .datepicker table tr td,
.table-striped .datepicker table tr th {
background-color: transparent;
}
.datepicker table tr td.day:hover,
.datepicker table tr td.day.focused {
background: #eee;
cursor: pointer;
}
.datepicker table tr td.old,
.datepicker table tr td.new {
color: #999;
}
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover {
background: none;
color: #999;
cursor: default;
}
.datepicker table tr td.highlighted {
background: #d9edf7;
border-radius: 0;
}
.datepicker table tr td.today,
.datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:hover {
background-color: #fde19a;
background-image: -moz-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -ms-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
background-image: -webkit-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: -o-linear-gradient(to bottom, #fdd49a, #fdf59a);
background-image: linear-gradient(to bottom, #fdd49a, #fdf59a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #000;
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today.disabled:hover:hover,
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today:hover.disabled,
.datepicker table tr td.today.disabled.disabled,
.datepicker table tr td.today.disabled:hover.disabled,
.datepicker table tr td.today[disabled],
.datepicker table tr td.today:hover[disabled],
.datepicker table tr td.today.disabled[disabled],
.datepicker table tr td.today.disabled:hover[disabled] {
background-color: #fdf59a;
}
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active {
background-color: #fbf069 \9;
}
.datepicker table tr td.today:hover:hover {
color: #000;
}
.datepicker table tr td.today.active:hover {
color: #fff;
}
.datepicker table tr td.range,
.datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:hover {
background: #eee;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today,
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:hover {
background-color: #f3d17a;
background-image: -moz-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -ms-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
background-image: -webkit-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: -o-linear-gradient(to bottom, #f3c17a, #f3e97a);
background-image: linear-gradient(to bottom, #f3c17a, #f3e97a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
border-color: #f3e97a #f3e97a #edde34;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today:hover:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today:hover.disabled,
.datepicker table tr td.range.today.disabled.disabled,
.datepicker table tr td.range.today.disabled:hover.disabled,
.datepicker table tr td.range.today[disabled],
.datepicker table tr td.range.today:hover[disabled],
.datepicker table tr td.range.today.disabled[disabled],
.datepicker table tr td.range.today.disabled:hover[disabled] {
background-color: #f3e97a;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active {
background-color: #efe24b \9;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected.disabled:hover {
background-color: #9e9e9e;
background-image: -moz-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -ms-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
background-image: -webkit-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: -o-linear-gradient(to bottom, #b3b3b3, #808080);
background-image: linear-gradient(to bottom, #b3b3b3, #808080);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
border-color: #808080 #808080 #595959;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected:hover:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected:hover.disabled,
.datepicker table tr td.selected.disabled.disabled,
.datepicker table tr td.selected.disabled:hover.disabled,
.datepicker table tr td.selected[disabled],
.datepicker table tr td.selected:hover[disabled],
.datepicker table tr td.selected.disabled[disabled],
.datepicker table tr td.selected.disabled:hover[disabled] {
background-color: #808080;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active {
background-color: #666666 \9;
}
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
background-image: linear-gradient(to bottom, #08c, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.active:hover,
.datepicker table tr td.active:hover:hover,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.disabled:hover:hover,
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active:hover.disabled,
.datepicker table tr td.active.disabled.disabled,
.datepicker table tr td.active.disabled:hover.disabled,
.datepicker table tr td.active[disabled],
.datepicker table tr td.active:hover[disabled],
.datepicker table tr td.active.disabled[disabled],
.datepicker table tr td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datepicker table tr td span:hover,
.datepicker table tr td span.focused {
background: #eee;
}
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
background: none;
color: #999;
cursor: default;
}
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
background-image: linear-gradient(to bottom, #08c, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active:hover:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active:hover.disabled,
.datepicker table tr td span.active.disabled.disabled,
.datepicker table tr td span.active.disabled:hover.disabled,
.datepicker table tr td span.active[disabled],
.datepicker table tr td span.active:hover[disabled],
.datepicker table tr td span.active.disabled[disabled],
.datepicker table tr td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span.old,
.datepicker table tr td span.new {
color: #999;
}
.datepicker .datepicker-switch {
width: 145px;
}
.datepicker .datepicker-switch,
.datepicker .prev,
.datepicker .next,
.datepicker tfoot tr th {
cursor: pointer;
}
.datepicker .datepicker-switch:hover,
.datepicker .prev:hover,
.datepicker .next:hover,
.datepicker tfoot tr th:hover {
background: #eee;
}
.datepicker .prev.disabled,
.datepicker .next.disabled {
visibility: hidden;
}
.datepicker .cw {
font-size: 10px;
width: 12px;
padding: 0 2px 0 5px;
vertical-align: middle;
}
.input-append.date .add-on,
.input-prepend.date .add-on {
cursor: pointer;
}
.input-append.date .add-on i,
.input-prepend.date .add-on i {
margin-top: 3px;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
-webkit-border-radius: 3px 0 0 3px;
-moz-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
-webkit-border-radius: 0 3px 3px 0;
-moz-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0;
}
.input-daterange .add-on {
display: inline-block;
width: auto;
min-width: 16px;
height: 20px;
padding: 4px 5px;
font-weight: normal;
line-height: 20px;
text-align: center;
text-shadow: 0 1px 0 #fff;
vertical-align: middle;
background-color: #eee;
border: 1px solid #ccc;
margin-left: -5px;
margin-right: -5px;
}
.datepicker.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
float: left;
display: none;
min-width: 160px;
list-style: none;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
*border-right-width: 2px;
*border-bottom-width: 2px;
color: #333333;
font-size: 13px;
line-height: 20px;
}
.datepicker.dropdown-menu th,
.datepicker.datepicker-inline th,
.datepicker.dropdown-menu td,
.datepicker.datepicker-inline td {
padding: 4px 5px;
}
/*# sourceMappingURL=bootstrap-datepicker.standalone.css.map */

@ -0,0 +1,36 @@
class Admins::DailySchoolStatisticsController < Admins::BaseController
def index
params[:sort_by] = params[:sort_by].presence || :teacher_count
params[:sort_direction] = params[:sort_direction].presence || :desc
total_count, statistics = Admins::SchoolDailyStatisticService.call(params)
@statistics = paginate statistics, total_count: total_count
respond_to do |format|
format.html { load_statistic_total }
format.js
end
end
def export
params[:per_page] = 10000
_count, @schools = Admins::SchoolDailyStatisticService.call(params)
filename = ['学校统计总表', params[:keyword], Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
private
def load_statistic_total
@teacher_total = User.joins(:user_extension).where(user_extensions: { identity: :teacher }).count
@student_total = User.joins(:user_extension).where(user_extensions: { identity: :student }).count
@course_total = Course.count
@active_course_total = Course.where(is_end: false).count
@shixun_homework_total = HomeworkCommon.where(homework_type: 4).count
@other_homework_total = HomeworkCommon.where(homework_type: [1, 3]).count
@shixun_total = Shixun.count
@shixun_evaluate_total = SchoolReport.sum(:shixun_evaluate_count)
end
end

@ -0,0 +1,49 @@
class Admins::SchoolStatisticsController < Admins::BaseController
before_action :contrast_column_select_options, only: [:contrast]
def index
params[:data_type] ||= 'grow'
params[:sort_by] = params[:sort_by].presence || :teacher_increase_count
params[:sort_direction] = params[:sort_direction].presence || :desc
service = Admins::StatisticSchoolDataGrowService.new(params)
@grow_summary = service.grow_summary
total_count, statistics = service.call
@statistics = paginate statistics, total_count: total_count
end
def contrast
params[:contrast_column] = params[:contrast_column].presence || :teacher_increase_count
params[:sort_direction] ||= :desc
params[:sort_by] = :percentage
# 无对比日期时直接返回无数据页面
if useless_contrast_date_parameter?
@total_count = 0
@statistics = paginate([])
return
end
total_count, statistics = Admins::StatisticSchoolContrastDataService.call(params)
@statistics = paginate statistics, total_count: total_count
rescue Admins::StatisticSchoolContrastDataService::ParameterError
render_unprocessable_entity('参数错误')
end
private
def useless_contrast_date_parameter?
params[:begin_date].blank? && params[:end_date].blank? &&
params[:other_begin_date].blank? &&params[:other_end_date].blank?
end
def contrast_column_select_options
@select_options =
Admins::StatisticSchoolContrastDataService::CONTRAST_COLUMN_LIST.map do |column|
[I18n.t("school_daily_report.#{column}"), column]
end
end
end

@ -3,6 +3,8 @@ module Admins::ErrorRescueHandler
included do
rescue_from Exception, Educoder::TipException do |e|
raise e if Rails.env.development?
Util.logger_error e
internal_server_error
end

@ -1,6 +1,10 @@
module Admins::PaginateHelper
extend ActiveSupport::Concern
def offset
(page - 1) * per_page
end
def page
params[:page].to_i <= 0 ? 1 : params[:page].to_i
end
@ -8,8 +12,13 @@ module Admins::PaginateHelper
def per_page
params[:per_page].to_i <= 0 || params[:per_page].to_i > 100 ? 20 : params[:per_page].to_i
end
alias_method :limit, :per_page
def paginate(relations)
relations.page(page).per(per_page)
def paginate(relations, total_count: nil)
if relations.is_a?(Array)
Kaminari.paginate_array(relations, limit: limit, offset: offset, total_count: total_count)
else
relations.page(page).per(per_page)
end
end
end

@ -10,6 +10,9 @@ class School < ApplicationRecord
has_many :ec_major_schools, :dependent => :destroy
has_many :ec_majors, :through => :ec_major_schools
has_many :school_daily_reports, dependent: :destroy
has_many :courses
# 学校管理员
def manager?(user)
ec_school_users.exists?(user_id: user.id)

@ -0,0 +1,3 @@
class SchoolDailyActiveUser < ApplicationRecord
belongs_to :school_daily_report
end

@ -0,0 +1,5 @@
class SchoolDailyReport < ApplicationRecord
belongs_to :school
has_many :school_daily_active_users, dependent: :delete_all
end

@ -0,0 +1,3 @@
class SchoolReport < ApplicationRecord
belongs_to :school
end

@ -0,0 +1,123 @@
class Admins::SchoolDailyStatisticService < ApplicationService
include CustomSortable
attr_reader :params
sort_columns :student_count, :teacher_count, :homework_count, :other_homework_count,
:course_count, :active_course_count, :nearly_course_time, :shixun_count, :shixun_evaluate_count,
default_by: :teacher_count, default_direction: :desc
def initialize(params)
@params = params
end
def call
schools = School.group('schools.id')
keyword = params[:keyword].try(:to_s).try(:strip)
if keyword.present?
schools = schools.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
end
count = schools.count.count
# 根据排序字段进行查询
schools = query_by_sort_column(schools, params[:sort_by])
schools = custom_sort(schools, params[:sort_by], params[:sort_direction])
schools = schools.limit(page_size).offset(offset)
# 查询并组装其它数据
schools = package_other_data(schools)
[count, schools]
end
def package_other_data(schools)
ids = schools.map(&:id)
student_map = UserExtension.where(school_id: ids, identity: :student).group(:school_id).count
teacher_map = UserExtension.where(school_id: ids, identity: :teacher).group(:school_id).count
homeworks = HomeworkCommon.joins(:course)
shixun_homework_map = homeworks.where(homework_type: 4, courses: { school_id: ids }).group('school_id').count
other_homework_map = homeworks.where(homework_type: [1, 3], courses: { school_id: ids }).group('school_id').count
courses = Course.where(is_delete: 0, school_id: ids).group('school_id')
course_map = courses.count
nearly_course_time_map = courses.joins(:course_acts).maximum('course_activities.updated_at')
active_course_map = courses.where(is_end: false).count
shixun_map = Shixun.joins(user: :user_extension).where(user_extensions: { identity: :teacher, school_id: ids })
.where(fork_from: nil).group('school_id').count
reports = SchoolReport.where(school_id: ids)
evaluate_count_map = reports.each_with_object({}) { |report, obj| obj[report.school_id] = report.shixun_evaluate_count }
schools.map do |school|
{
id: school.id,
name: school.name,
teacher_count: teacher_map[school.id],
student_count: student_map[school.id],
homework_count: shixun_homework_map[school.id],
other_homework_count: other_homework_map[school.id],
course_count: course_map[school.id],
nearly_course_time: nearly_course_time_map[school.id],
active_course_count: active_course_map[school.id],
shixun_count: shixun_map.fetch(school.id, 0),
shixun_evaluate_count: evaluate_count_map.fetch(school.id, 0)
}
end
end
private
def query_by_sort_column(schools, sort_by_column)
base_query_column = 'schools.id, schools.name'
case sort_by_column.to_s
when 'teacher_count' then
schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
.select("#{base_query_column}, COUNT(*) teacher_count")
when 'student_count' then
schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 1')
.select("#{base_query_column}, COUNT(*) student_count")
when 'homework_count' then
schools.joins('LEFT JOIN courses ON courses.school_id = schools.id')
.joins('LEFT JOIN homework_commons hc ON hc.course_id = courses.id AND hc.homework_type = 4')
.select("#{base_query_column}, COUNT(*) homework_count")
when 'other_homework_count' then
schools.joins('LEFT JOIN courses ON courses.school_id = schools.id')
.joins('LEFT JOIN homework_commons hc ON hc.course_id = courses.id AND hc.homework_type IN (1, 3)')
.select("#{base_query_column}, COUNT(*) other_homework_count")
when 'course_count' then
schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0')
.select("#{base_query_column}, COUNT(*) course_count")
when 'shixun_count' then
schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
.joins('LEFT JOIN users ON users.id = ue.user_id')
.joins('LEFT JOIN shixuns sx ON sx.user_id = users.id AND sx.fork_from IS NULL')
.select("#{base_query_column}, COUNT(*) shixun_count")
when 'shixun_evaluate_count' then
schools.joins('LEFT JOIN school_reports ON school_reports.school_id = schools.id')
.select("#{base_query_column}, shixun_evaluate_count")
when 'nearly_course_time' then
schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0')
.joins('LEFT JOIN course_activities acs ON acs.course_id = cs.id')
.select("#{base_query_column}, MAX(acs.updated_at) nearly_course_time")
when 'active_course_count' then
schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0 AND cs.is_end = false')
.select("#{base_query_column}, COUNT(*) active_course_count")
else
schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
.select("#{base_query_column}, COUNT(*) teacher_count")
end
end
def page_size
params[:per_page] || 20
end
def offset
(params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * page_size
end
end

@ -0,0 +1,80 @@
class Admins::StatisticSchoolContrastDataService < ApplicationService
ParameterError = Class.new(StandardError)
PAGE_SIZE = 20
CONTRAST_COLUMN_LIST = %w(
teacher_increase_count student_increase_count course_increase_count
shixun_increase_count active_user_count shixun_homework_count shixun_evaluate_count
).freeze
attr_reader :params, :sort_direction, :contrast_column
def initialize(params)
@params = params
@sort_direction = params[:sort_direction].to_s
@contrast_column = params[:contrast_column].to_s
end
def call
validate_parameter!
reports = School.joins(:school_daily_reports).select(select_columns)
keyword = params[:keyword].try(:to_s).try(:strip)
if keyword.present?
reports = reports.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
end
count = reports.count('distinct(schools.id)')
sql = query_report_sql(reports.group('schools.id').to_sql)
reports = SchoolDailyReport.find_by_sql(sql)
[count, reports]
end
private
def validate_parameter!
if %i[begin_date end_date other_begin_date other_end_date].any? { |key| params[key].blank? }
raise ParameterError
end
unless %w(desc asc).include?(sort_direction)
raise ParameterError
end
unless CONTRAST_COLUMN_LIST.include?(contrast_column)
raise ParameterError
end
end
def format_date(date)
Time.zone.parse(date).strftime("%Y-%m-%d")
end
def offset
(params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * PAGE_SIZE
end
def select_columns
if contrast_column != 'active_user_count'
"schools.id school_id, schools.name school_name,"\
"(SUM(IF(date BETWEEN '#{format_date(params[:begin_date])}' AND '#{format_date(params[:end_date])}', #{contrast_column}, 0))) total,"\
"(SUM(IF(date BETWEEN '#{format_date(params[:other_begin_date])}' AND '#{format_date(params[:other_end_date])}', #{contrast_column}, 0))) other_total"
else
# 活跃用户对比时处理方法不同
relations = SchoolDailyActiveUser.select('COUNT(distinct user_id)').joins(:school_daily_report)
.where('school_id = schools.id')
total_subquery = relations.where("date BETWEEN '#{format_date(params[:begin_date])}' AND '#{format_date(params[:end_date])}'").to_sql
other_total_subquery = relations.where("date BETWEEN '#{format_date(params[:other_begin_date])}' AND '#{format_date(params[:other_end_date])}'").to_sql
"schools.id school_id, schools.name school_name, (#{total_subquery}) AS total, (#{other_total_subquery}) AS other_total"
end
end
def query_report_sql(from_sql)
order_by = "(total = 0 AND other_total != 0) #{sort_direction}, (percentage != 0) #{sort_direction}, percentage #{sort_direction}"
"SELECT reports.*, (other_total - total) increase, (IF(other_total - total = 0, 0.0, round((other_total - total) / IF(total = 0, 1, total), 5))) percentage "\
"FROM (#{from_sql}) reports ORDER BY #{order_by} LIMIT #{PAGE_SIZE} OFFSET #{offset}"
end
end

@ -0,0 +1,107 @@
class Admins::StatisticSchoolDataGrowService < ApplicationService
include CustomSortable
PAGE_SIZE = 20
attr_reader :params
sort_columns :teacher_increase_count, :student_increase_count,
:course_increase_count, :shixun_increase_count, :uniq_active_user_count,
:shixun_homework_count, :shixun_evaluate_count,
default_by: :teacher_increase_count, default_direction: :desc
def initialize(params)
@params = params
end
def call
reports = School.where(nil)
reports = search_filter(reports)
count = reports.count
subquery = SchoolDailyActiveUser.select('COUNT(distinct(user_id))').joins(:school_daily_report)
.where(date_condition_sql).where("school_id is not null and school_id = schools.id").to_sql
reports = reports.joins("LEFT JOIN school_daily_reports sdr ON sdr.school_id = schools.id AND #{date_condition_sql}")
reports = reports.select(
'schools.id school_id, schools.name school_name,'\
'SUM(teacher_increase_count) teacher_increase_count,'\
'SUM(student_increase_count) student_increase_count,'\
'SUM(course_increase_count) course_increase_count,'\
'SUM(shixun_increase_count) shixun_increase_count,'\
'SUM(shixun_homework_count) shixun_homework_count,'\
'SUM(shixun_evaluate_count) shixun_evaluate_count,'\
"(#{subquery}) uniq_active_user_count,"\
'SUM(active_user_count) active_user_count').group('schools.id')
reports = custom_sort(reports, params[:sort_by], params[:sort_direction])
reports = reports.order('school_id asc').limit(PAGE_SIZE).offset(offset)
[count, reports]
end
def grow_summary
@_grow_summary ||= begin
reports = School.joins("LEFT JOIN school_daily_reports sdr ON sdr.school_id = schools.id")
.where(date_condition_sql)
subquery = SchoolDailyActiveUser.select('COUNT(distinct user_id)')
.joins('LEFT JOIN school_daily_reports sdr ON sdr.id = school_daily_active_users.school_daily_report_id')
.where(date_condition_sql).to_sql
reports = search_filter(reports)
reports.select(
'SUM(teacher_increase_count) teacher_increase_count,'\
'SUM(student_increase_count) student_increase_count,'\
'SUM(course_increase_count) course_increase_count,'\
'SUM(shixun_increase_count) shixun_increase_count,'\
'SUM(shixun_homework_count) shixun_homework_count,'\
'SUM(shixun_evaluate_count) shixun_evaluate_count,'\
"(#{subquery}) uniq_active_user_count,"\
'SUM(active_user_count) active_user_count'
).first
end
end
private
def search_filter(relations)
keyword = params[:keyword].try(:to_s).try(:strip)
if keyword.present?
relations = relations.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
end
relations
end
def date_condition_sql
date = query_date
if date.is_a?(Range)
"date BETWEEN '#{date.min.strftime('%Y-%m-%d')}' AND '#{date.max.strftime('%Y-%m-%d')}'"
else
"date = '#{date.strftime('%Y-%m-%d')}'"
end
end
def query_date
if params[:grow_begin_date].present?
begin_time = Time.zone.parse(params[:grow_begin_date])
end_date = if params[:grow_end_date].present?
Time.zone.parse(params[:grow_end_date])
end
end_date.blank? || end_date == begin_time ? begin_time : begin_time..end_date
else
yesterday
end
end
def yesterday
# 每日凌晨5点为节点, 25日凌晨4点、3点、2点等等未到更新数据时间点看到的数据是23日-24日的统计数据
(Time.zone.now - 5.hours).beginning_of_day - 1.days
end
def offset
(params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * PAGE_SIZE
end
end

@ -0,0 +1,69 @@
class StatisticSchoolDailyReportTask
def call
School.find_each do |school|
# 新增教师和学生
users = User.joins(:user_extension)
.where(user_extensions: { school_id: school.id })
teacher_count = users.where(created_on: yesterday, user_extensions: { identity: :teacher }).count
student_count = users.where(created_on: yesterday, user_extensions: { identity: :student }).count
# 活跃用户
active_user_ids = users.where(last_login_on: yesterday).pluck(:id)
active_user_count = active_user_ids.size
# 新增课堂
course_count = school.courses.where(created_at: yesterday).count
# 新增实训
shixun_count = Shixun.joins(user: :user_extension)
.where(user_extensions: { identity: :teacher, school_id: school.id })
.where(created_at: yesterday).count
# 新增实训作业数
shixun_homework_count = HomeworkCommon.joins(:course).where(courses: { school_id: school.id })
.where(homework_type: 4, created_at: yesterday).count
# 新增实训评测数量
shixun_evaluate_count = EvaluateRecord.joins('LEFT JOIN homework_commons_shixuns hcs ON hcs.shixun_id = evaluate_records.shixun_id')
.joins('LEFT JOIN homework_commons hc ON hcs.homework_common_id = hc.id AND hc.homework_type = 4')
.joins('LEFT JOIN members ON members.user_id = evaluate_records.user_id')
.joins('LEFT JOIN courses ON members.course_id = courses.id AND hc.course_id = courses.id')
.where(courses: { school_id: school.id })
.where(created_at: yesterday).reorder(nil).count
# 无有效数据时不记录
data = [teacher_count, student_count, course_count, shixun_count, active_user_count,
shixun_homework_count, shixun_evaluate_count]
next if data.all?(&:zero?)
create_params = {
school_id: school.id, school_name: school.name, teacher_increase_count: teacher_count,
student_increase_count: student_count, course_increase_count: course_count,
shixun_homework_count: shixun_homework_count, shixun_evaluate_count: shixun_evaluate_count,
shixun_increase_count: shixun_count, active_user_count: active_user_count, date: current_date
}
report = SchoolDailyReport.create!(create_params)
if active_user_ids.present?
values = '(' + active_user_ids.join(", #{report.id}),(") + ", #{report.id})"
user_sql = "INSERT INTO school_daily_active_users(user_id, school_daily_report_id) VALUES#{values}"
SchoolDailyActiveUser.connection.execute(user_sql)
end
end
end
private
def current_date
@_current_date ||= Time.zone.now.beginning_of_day - 1.day
end
def yesterday
@_yesterday ||= begin
# 每日凌晨5点为节点
end_time = Time.zone.now.beginning_of_day + 5.hour
begin_time = end_time - 1.day
begin_time..end_time
end
end
end

@ -0,0 +1,20 @@
class StatisticSchoolReportTask
def call
School.find_each do |school|
evaluate_count = Game.joins(:challenge)
.joins('LEFT JOIN members ON members.user_id = games.user_id')
.joins('LEFT JOIN homework_commons_shixuns hcs ON hcs.shixun_id = challenges.shixun_id')
.joins('LEFT JOIN homework_commons hc ON hcs.homework_common_id = hc.id AND hc.homework_type = 4')
.joins('LEFT JOIN courses ON hc.course_id = courses.id AND members.course_id = courses.id')
.where(courses: { school_id: school.id })
.sum(:evaluate_count)
report = SchoolReport.find_or_initialize_by(school_id: school.id)
report.school_name = school.name
report.shixun_evaluate_count = evaluate_count
report.save
end
end
end

@ -0,0 +1,13 @@
wb = xlsx_package.workbook
wb.add_worksheet(name: '统计总表') do |sheet|
sheet.add_row %w(ID 单位名称 教师总人数 学生总人数 课堂总数 正在进行课堂数 总实训数 实训评测总数 实训作业总数 其它作业总数 动态时间)
@schools.each do |school|
sheet.add_row([
school[:id].to_s, school[:name].to_s, (school[:teacher_count] || 0).to_s, (school[:student_count] || 0).to_s,
(school[:course_count] || 0).to_s, (school[:active_course_count] || 0).to_s,
(school[:shixun_count] || 0).to_s,(school[:shixun_evaluate_count] || 0).to_s, (school[:homework_count] || 0).to_s,
(school[:other_homework_count] || 0).to_s, format_time(school[:nearly_course_time])
])
end
end

@ -0,0 +1,29 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('统计总表', admins_daily_school_statistics_path) %>
<% end %>
<div class="box search-form-container daily-school-statistic-list-form">
<%= form_tag(admins_daily_school_statistics_path, method: :get, class: 'form-inline search-form', remote: true) do %>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'ID/单位名称搜索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
<%#= link_to '导出Excel', export_admins_daily_school_statistics_path(format: :xlsx), class: 'btn btn-outline-primary export-action' %>
<%= javascript_void_link '导出Excel', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_daily_school_statistics_path(format: :xlsx) %>
</div>
<div class="box py-0 pt-4 pl-4">
统计总计:
教师总人数<span class="text-danger"><%= @teacher_total %></span>人,
学生总人数<span class="text-danger"><%= @student_total %></span>人,
课堂总数<span class="text-danger"><%= @course_total %></span>个,
正在进行课堂总数<span class="text-danger"><%= @active_course_total %></span>个,
实训总数<span class="text-danger"><%= @shixun_total %></span>个,
实训评测总数<span class="text-danger"><%= @shixun_evaluate_total %></span>个,
实训作业总数<span class="text-danger"><%= @shixun_homework_total %></span>个,
其它作业总数<span class="text-danger"><%= @other_homework_total %></span>个
</div>
<div class="box daily-school-statistic-list-container">
<%= render partial: 'admins/daily_school_statistics/shared/list', locals: { statistics: @statistics } %>
</div>

@ -0,0 +1 @@
$(".daily-school-statistic-list-container").html("<%= j(render partial: 'admins/daily_school_statistics/shared/list', locals: { statistics: @statistics }) %>")

@ -0,0 +1,46 @@
<table class="table table-hover daily-school-statistic-list-table">
<thead class="thead-light">
<tr>
<th width="18%" class="edu-txt-left">单位名称</th>
<th width="10%"><%= sort_tag('教师总人数', name: 'teacher_count', path: admins_daily_school_statistics_path) %></th>
<th width="10%"><%= sort_tag('学生总人数', name: 'student_count', path: admins_daily_school_statistics_path) %></th>
<th width="10%"><%= sort_tag('课堂总数', name: 'course_count', path: admins_daily_school_statistics_path) %></th>
<th width="10%"><%= sort_tag('正在进行课堂数', name: 'active_course_count', path: admins_daily_school_statistics_path) %></th>
<th width="10%"><%= sort_tag('实训总数', name: 'shixun_count', path: admins_daily_school_statistics_path) %></th>
<th width="12%">
<%= sort_tag('实训评测总数', name: 'shixun_evaluate_count', path: admins_daily_school_statistics_path) %>
<i class="fa fa-question-circle" data-toggle="tooltip" data-html="true" data-placement="top" title="数据更新时间为<br/>当日6点、12点、18点、24点"></i>
</th>
<th width="10%"><%= sort_tag('实训作业总数', name: 'homework_count', path: admins_daily_school_statistics_path) %></th>
<th width="10%"><%= sort_tag('其它作业总数', name: 'other_homework_count', path: admins_daily_school_statistics_path) %></th>
<th width="14%"><%= sort_tag('动态时间', name: 'nearly_course_time', path: admins_daily_school_statistics_path) %></th>
</tr>
</thead>
<tbody>
<% if statistics.present? %>
<% statistics.each do |statistic| %>
<tr>
<td class="edu-txt-left">
<%= link_to '#' do %>
<%= overflow_hidden_span statistic[:name], width: '200px' %>
<% end %>
</td>
<td><%= statistic[:teacher_count].to_i %></td>
<td><%= statistic[:student_count].to_i %></td>
<td><%= statistic[:course_count].to_i %></td>
<td><%= statistic[:active_course_count].to_i %></td>
<td><%= statistic[:shixun_count].to_i %></td>
<td><%= statistic[:shixun_evaluate_count].to_i %></td>
<td><%= statistic[:homework_count].to_i %></td>
<td><%= statistic[:other_homework_count].to_i %></td>
<td><%= statistic[:nearly_course_time]&.strftime('%Y-%m-%d %H:%M') %></td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %>

@ -0,0 +1 @@
$(".school-statistic-list-container").html("<%= j(render partial: 'admins/school_statistics/shared/contrast_list', locals: { statistics: @statistics, select_options: @select_options }) %>")

@ -0,0 +1,49 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('数据变化报表', admins_school_statistics_path) %>
<% end %>
<div class="box search-form-container school-statistic-list-form">
<form class="form-inline search-form d-flex" data-grow-form-url="<%= admins_school_statistics_path %>" data-contrast-form-url="<%= contrast_admins_school_statistics_path %>">
<div class="time-select">
<!-- 数据对比 -->
<%= hidden_field_tag :contrast_column, params[:contrast_column] %>
<div class="form-group grow-date-container" style="<%= params[:data_type] == 'grow' ? '' : 'display: none' %>">
<div class="input-group input-daterange grow-date-input-daterange">
<%= text_field_tag :grow_begin_date, params[:grow_begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %>
<div class="input-group-prepend"><span class="input-group-text">到</span></div>
<%= text_field_tag :grow_end_date, params[:grow_end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %>
</div>
</div>
<div class="contrast-date-container" style="<%= params[:data_type] == 'contrast' ? '' : 'display: none' %>">
<div class="input-group input-daterange date-input-daterange">
<%= text_field_tag :begin_date, params[:begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %>
<div class="input-group-prepend"><span class="input-group-text">到</span></div>
<%= text_field_tag :end_date, params[:end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %>
</div>
<div class="mx-2 align-middle">VS</div>
<div class="input-group input-daterange other-date-input-daterange">
<%= text_field_tag :other_begin_date, params[:other_begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %>
<div class="input-group-prepend"><span class="input-group-text">到</span></div>
<%= text_field_tag :other_end_date, params[:other_end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %>
</div>
</div>
</div>
<div class="type-box ml-3">
<%= hidden_field_tag :data_type, params[:data_type] || 'grow' %>
<%= javascript_void_link '时段对比', class: "btn btn-outline-primary btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}",
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'}",
data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧选择时间段具体从当日5:00开始计算下表显示选定时间段内各项指标数据变化情况' } %>
</div>
<%= text_field_tag :keyword, params[:keyword], placeholder: 'ID/单位名称检索', class: 'form-control mx-3 search-input' %>
<%= javascript_void_link '搜索', class: 'btn btn-primary search-btn' %>
</form>
</div>
<div class="box school-statistic-list-container">
<%= render partial: 'admins/school_statistics/shared/list', locals: { statistics: @statistics } %>
</div>

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

@ -0,0 +1,71 @@
<div style="position: relative;">
<div style="padding: 15px 0; text-align: center; font-size: 18px; font-weight: bold; border: 1px solid #eee; border-bottom: unset;">
学校数据统计(<%= I18n.t("school_daily_report.#{params[:contrast_column]}") %>变化统计情况)
</div>
<%= select_tag :contrast_column,
options_for_select(select_options, params[:contrast_column]),
class: 'form-control contrast-column-select' %>
<% if statistics.present? %>
<div style="position: absolute; left: 20px; bottom: 0; font-size: 12px;">
说明:新增数=时段二-时段一;新增百分比=(新增数/时段一)*100%(保留小数点后五位)
</div>
<% end %>
</div>
<% if statistics.present? %>
<table class="table table-hover text-center daily-school-statistic-list-table">
<thead>
<tr>
<th width="36%" class="text-left">单位名称</th>
<th width="22%">时段一<br><%= "#{params[:begin_date]} 05:00至#{(Time.zone.parse(params[:end_date]) + 1.days).strftime('%Y-%m-%d')} 05:00" %></th>
<th width="22%">时段二<br><%= "#{params[:other_begin_date]} 05:00至#{(Time.zone.parse(params[:other_end_date]) + 1.days).strftime('%Y-%m-%d')} 05:00" %></th>
<th width="20%" colspan="2">
<%= sort_tag('变化情况', name: 'percentage', path: contrast_admins_school_statistics_path) %>
<br> 新 增 数 | 新增百分比)
</th>
</tr>
</thead>
<tbody>
<% statistics.each do |statistic| %>
<tr>
<td class="text-left">
<%= link_to statistic.school_name, '' %>
</td>
<td><%= statistic['total'] %></td>
<td><%= statistic['other_total'] %></td>
<%
increase = statistic['other_total'] - statistic['total']
percentage = statistic['total'].zero? ? increase.to_f * 100 : (increase / statistic['total'].to_f) * 100
%>
<% if increase > 0 %>
<td class="text-right pr-2 text-danger relative right-border">
+<%= increase %>
</td>
<% if statistic['total'].zero? %>
<td class="text-left pl-2">-</td>
<% else %>
<td class="text-left pl-2 text-danger">+<%= percentage.round(5) %>%</td>
<% end %>
<% elsif increase.zero? %>
<td class="text-right pr-2 right-border relative" style="position: relative;">
<%= increase %>
</td>
<td class="text-left pl-2"><%= percentage.round(5) %>%</td>
<% else %>
<td class="text-right pr-2 right-border relative text-success">
<%= increase %>
</td>
<td class="text-left pl-2 text-success"><%= percentage.round(5) %>%</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %>
<% else %>
<div style="padding-top: 150px; height: 250px; text-align: center; color: grey; font-size: 14px;">
暂无数据,请选择时间段对比
</div>
<% end %>

@ -0,0 +1,55 @@
<div class="box pb-4 pt-0 pl-0">
统计总计:
<% if params[:grow_begin_date].present? %>
<%= params[:grow_begin_date] %> 05:00至<%= (Time.zone.parse(params[:grow_end_date]) + 1.days).strftime('%Y-%m-%d') %> 05:00
<% else %>
<%= (Time.current - 5.hour).beginning_of_day.ago(1.days).strftime('%Y-%m-%d') %> 05:00至
<%= (Time.current - 5.hour).beginning_of_day.strftime('%Y-%m-%d') %> 05:00
<% end %>
新增教师<span class="text-danger"><%= @grow_summary.teacher_increase_count || 0 %></span>人,
新增学生<span class="text-danger"><%= @grow_summary.student_increase_count || 0 %></span>人,
新增课堂<span class="text-danger"><%= @grow_summary.course_increase_count || 0 %></span>个,
新增实训<span class="text-danger"><%= @grow_summary.shixun_increase_count || 0 %></span>个,
新增实训作业<span class="text-danger"><%= @grow_summary.shixun_homework_count || 0 %></span>个,
新增实训评测<span class="text-danger"><%= @grow_summary.shixun_evaluate_count || 0 %></span>个,
活跃用户<span class="text-danger">
<%= @grow_summary.uniq_active_user_count.to_i.zero? ? @grow_summary.active_user_count.to_i : @grow_summary.uniq_active_user_count.to_i %>
</span>个
</div>
<table class="table table-hover text-center daily-school-statistic-list-table">
<thead class="thead-light">
<tr>
<th width="24%" class="text-left">单位名称</th>
<th width="12%"><%= sort_tag('新增教师', name: 'teacher_increase_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('新增学生', name: 'student_increase_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('新增课堂', name: 'course_increase_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('新增实训', name: 'shixun_increase_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('新增实训作业', name: 'shixun_homework_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('新增实训评测', name: 'shixun_evaluate_count', path: admins_school_statistics_path) %></th>
<th width="12%"><%= sort_tag('活跃用户', name: 'uniq_active_user_count', path: admins_school_statistics_path) %></th>
</tr>
</thead>
<tbody>
<% if statistics.present? %>
<% statistics.each do |statistic| %>
<tr>
<td class="text-left">
<%= link_to statistic.school_name, '' %>
</td>
<td><%= statistic.teacher_increase_count.to_i %></td>
<td><%= statistic.student_increase_count.to_i %></td>
<td><%= statistic.course_increase_count.to_i %></td>
<td><%= statistic.shixun_increase_count.to_i %></td>
<td><%= statistic.shixun_homework_count.to_i %></td>
<td><%= statistic.shixun_evaluate_count.to_i %></td>
<td><%= statistic.uniq_active_user_count.to_i.zero? ? statistic.active_user_count.to_i : statistic.uniq_active_user_count.to_i %></td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %>

@ -1,6 +1,6 @@
<div class="d-flex flex-column align-items-center justify-content-center not-found">
<div class="not-found-img">
<div class="d-flex flex-column align-items-center justify-content-center global-error">
<div class="global-error-code">
<span>404</span>
</div>
<div class="not-found-text">资源未找到</div>
<div class="global-error-text">资源未找到</div>
</div>

@ -1,6 +1,6 @@
<div class="d-flex flex-column align-items-center justify-content-center not-found">
<div class="not-found-img">
<div class="d-flex flex-column align-items-center justify-content-center global-error">
<div class="global-error-code">
<span>422</span>
</div>
<div class="not-found-text"><%= @message %></div>
<div class="global-error-text"><%= @message %></div>
</div>

@ -1,6 +1,6 @@
<div class="d-flex flex-column align-items-center justify-content-center not-found">
<div class="not-found-img">
<div class="d-flex flex-column align-items-center justify-content-center global-error">
<div class="global-error-code">
<span>500</span>
</div>
<div class="not-found-text">系统错误</div>
<div class="global-error-text">系统错误</div>
</div>

@ -1,5 +1,5 @@
<% sidebar_collapse = request.cookies['admin_sidebar_collapse'].to_s == 'true' %>
<nav id="sidebar" class="<%= sidebar_collapse ? 'active' : '' %>">
<nav id="sidebar" class="<%= sidebar_collapse ? 'active' : '' %>" data-current-controller="<%= admin_sidebar_controller %>">
<div class="sidebar-header">
<a href="/" class="sidebar-header-logo" data-toggle="tooltip" title="返回主站">
<%= image_tag('logo.png') %>
@ -15,8 +15,8 @@
<li><%= sidebar_item(admins_path, '概览', icon: 'dashboard', controller: 'admins-dashboards') %></li>
<li>
<%= sidebar_item_group('#school-submenu', '学校统计', icon: 'area-chart') do %>
<li><%= sidebar_item('#', '统计总表', icon: 'bar-chart', controller: '') %></li>
<li><%= sidebar_item('#', '数据变化报表', icon: 'line-chart', controller: '') %></li>
<li><%= sidebar_item(admins_daily_school_statistics_path, '统计总表', icon: 'bar-chart', controller: 'admins-daily_school_statistics') %></li>
<li><%= sidebar_item(admins_school_statistics_path, '数据变化报表', icon: 'line-chart', controller: 'admins-school_statistics') %></li>
<% end %>
</li>
@ -39,25 +39,4 @@
<li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li>
</ul>
</nav>
<script>
$(function(){
$('#sidebarCollapse').on('click', function () {
$(this).toggleClass('active');
$('#sidebar').toggleClass('active');
$.cookie('admin_sidebar_collapse', $(this).hasClass('active'), { path: '/admins' });
});
});
$(document).on('turbolinks:load', function(){
var sidebarController = '<%= admin_sidebar_controller %>';
if (sidebarController.length > 0) {
$('#sidebar a.active').removeClass('active');
$('#sidebar ul.collapse.show').removeClass('show');
var activeLi = $('#sidebar a[data-controller="' + sidebarController + '"]');
activeLi.addClass('active');
activeLi.parent().parent('ul.collapse').addClass('show');
}
});
</script>
</nav>

@ -12,7 +12,7 @@
<%= javascript_include_tag 'admin', 'data-turbolinks-track': 'reload' %>
</head>
<% body_class = [params[:controller].gsub(/\//, '-'), params[:action], 'page'].join('-') %>
<% body_class = [params[:controller].gsub(/\//, '-').gsub('_', '-'), params[:action], 'page'].join('-') %>
<body class="<%= body_class %>">
<!-- Sidebar -->
<%= render partial: 'admins/shared/sidebar' %>

@ -0,0 +1,9 @@
zh-CN:
school_daily_report:
teacher_increase_count: 新增教师
student_increase_count: 新增学生
course_increase_count: 新增课堂
shixun_increase_count: 新增实训
shixun_homework_count: 新增实训作业
shixun_evaluate_count: 新增实训评测
active_user_count: 活跃用户

@ -8,7 +8,6 @@ Rails.application.routes.draw do
get 'attachments/download/:id/:filename', to: 'attachments#show'
resources :edu_settings
resources :admin
scope '/api' do
get 'home/index'
get 'home/search'
@ -742,6 +741,14 @@ Rails.application.routes.draw do
namespace :admins do
get '/', to: 'dashboards#index'
resources :daily_school_statistics, only: [:index] do
get :export, on: :collection
end
resources :school_statistics, only: [:index] do
get :contrast, on: :collection
end
resources :users, only: [:index, :edit, :update] do
member do
post :reward_grade

Loading…
Cancel
Save