Merge branch 'dev_aliyun' of http://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

dev_hs
cxt 5 years ago
commit 0d1256a6d8

@ -48,13 +48,11 @@ gem 'rqrcode_png'
gem 'acts-as-taggable-on', '~> 6.0'
group :development, :test do
#group :'development.rb.example', :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'rspec-rails', '~> 3.8'
end
group :development do
#group :'development.rb.example' do
gem 'awesome_print'
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
@ -78,6 +76,10 @@ gem 'faraday', '~> 0.15.4'
# view
gem 'active_decorator'
gem 'bootstrap', '~> 4.3.1'
gem 'jquery-rails'
gem 'simple_form'
gem 'font-awesome-sass', '4.7.0'
# i18n
gem 'rails-i18n', '~> 5.1'

@ -59,6 +59,8 @@ GEM
archive-zip (0.11.0)
io-like (~> 0.3.0)
arel (9.0.0)
autoprefixer-rails (9.6.1)
execjs
awesome_print (1.8.0)
axlsx (3.0.0.pre)
htmlentities (~> 4.3, >= 4.3.4)
@ -71,6 +73,10 @@ GEM
bindex (0.5.0)
bootsnap (1.3.1)
msgpack (~> 1.0)
bootstrap (4.3.1)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
sassc-rails (>= 2.0.0)
builder (3.2.3)
bulk_insert (1.7.0)
activerecord (>= 3.2.0)
@ -105,6 +111,8 @@ GEM
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
ffi (1.9.25)
font-awesome-sass (4.7.0)
sass (>= 3.2)
globalid (0.4.1)
activesupport (>= 4.2.0)
grape-entity (0.7.1)
@ -120,6 +128,10 @@ GEM
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
jquery-rails (4.3.5)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jwt (2.1.0)
kaminari (1.1.1)
activesupport (>= 4.1.0)
@ -165,6 +177,7 @@ GEM
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
pdfkit (0.8.4.1)
popper_js (1.14.5)
public_suffix (3.0.2)
puma (3.12.0)
rack (2.0.5)
@ -266,6 +279,15 @@ GEM
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sassc (2.0.1)
ffi (~> 1.9)
rake
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
searchkick (3.1.3)
activemodel (>= 4.2)
elasticsearch (>= 5)
@ -278,6 +300,9 @@ GEM
rack (>= 1.5.0)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
simple_form (4.1.0)
actionpack (>= 5.0)
activemodel (>= 5.0)
simple_xlsx_reader (1.0.4)
nokogiri
rubyzip
@ -336,14 +361,17 @@ DEPENDENCIES
axlsx (~> 3.0.0.pre)
axlsx_rails (~> 0.5.2)
bootsnap (>= 1.1.0)
bootstrap (~> 4.3.1)
bulk_insert
byebug
capybara (>= 2.15, < 4.0)
chromedriver-helper
faraday (~> 0.15.4)
font-awesome-sass (= 4.7.0)
gitlab!
grape-entity (~> 0.7.1)
jbuilder (~> 2.5)
jquery-rails
kaminari (~> 1.1, >= 1.1.1)
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
@ -366,6 +394,7 @@ DEPENDENCIES
searchkick
selenium-webdriver
sidekiq
simple_form
simple_xlsx_reader
sinatra
spreadsheet

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1,40 @@
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require jquery.validate.min
//= require additional-methods.min
//= require bootstrap-notify
//= require jquery.cookie.min
//= require select2
//= require jquery.cxselect
//= require bootstrap-datepicker
//= require_tree ./i18n
//= require_tree ./admins
// ******** select2 global config ********
$.fn.select2.defaults.set('theme', 'bootstrap4');
$.fn.select2.defaults.set('language', 'zh-CN');
$(document).on('turbolinks:load', function(){
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();
// flash alert提示框自动关闭
if($('.admin-alert-container .alert').length > 0){
setTimeout(function(){
$('.admin-alert-container .alert').alert('close');
}, 2000);
}
});
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide');
$('[data-toggle="popover"]').popover('hide');
});
$(function () {
});

@ -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');
}
});

@ -0,0 +1,156 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-users-edit-page, body.admins-users-update-page').length > 0) {
var initDepartmentSelect = true;
// ************** 学校选择 *************
var matcherFunc = function(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.name && data.name.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
var defineSchoolSelect = function (schools) {
$('.school-select').select2({
theme: 'bootstrap4',
placeholder: '查询学校/单位',
minimumInputLength: 1,
data: schools,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$('#user_school_id').val(item.id);
getDepartmentsData(item.id, defineDepartmentSelect2);
}
return item.name || item.text;
},
matcher: matcherFunc
});
};
var defineDepartmentSelect2 = function(departments){
departments.unshift({ id: '-1', name: '未选择' }); // 可不选
if (!initDepartmentSelect) { $('.department-select').empty(); } // 为了能够回填部门
initDepartmentSelect = false;
$('.department-select').select2({
theme: 'bootstrap4',
placeholder: '查询学院/部门',
minimumInputLength: 0,
data: departments,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$('#user_department_id').val(item.id);
}
return item.name || item.text;
},
matcher: matcherFunc
});
};
var getDepartmentsData = function(school_id, callback){
$.ajax({
url: '/api/schools/' + school_id + '/departments/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
callback(data.departments);
}
})
}
// 初始化学校选择器
$.ajax({
url: '/api/schools/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
defineSchoolSelect(data.schools);
}
});
// **************** 地区选择 ****************
$('.province-city-select').cxSelect({
url: '/javascripts/educoder/province-data.json',
selects: ['province-select', 'city-select']
});
// *********** 职业选择 ************
var identityData = [
{
"v": "teacher",
"n": "教师",
"s": [{"n": "教授", "v": "教授"},{"n": "副教授", "v": "副教授"},{"n": "讲师", "v": "讲师"},{"n": "助教", "v": "助教"}]
},
{
"v": "student",
"n": "学生",
"s": []
},
{
"v": "professional",
"n": "专业人士",
"s": [{"n": "企业管理者", "v": "企业管理者"},{"n": "部门管理者", "v": "部门管理者"},{"n": "高级工程师", "v": "高级工程师"},{"n": "工程师", "v": "工程师"},{"n": "助理工程师", "v": "助理工程师"}]
}
];
$('.user-identity-select').cxSelect({
data: identityData,
jsonValue: 'v',
selects: ['identity-select', 'technical-title-select']
});
$('.identity-select').on('change', function(){
if($(this).val() === 'student'){
$('.technical-title-select-wrapper').hide();
$('.form-group.user_student_id').show();
} else {
$('.technical-title-select-wrapper').show();
$('.form-group.user_student_id').hide();
}
})
var $form = $('form.edit_user')
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"user[password]": {
required: false,
minlength: 5
},
"user[password_confirmation]": {
required: false,
minlength: 5,
equalTo: "#user_password"
},
},
messages: {
"user[password_confirmation]": {
equalTo: "两次密码输入不一致"
}
}
})
$form.submit(function(e){
if(!$form.valid()){ e.preventDefault(); }
})
}
});

@ -0,0 +1,121 @@
$(document).on('turbolinks:load', function(){
if ($('body.admins-users-index-page').length > 0) {
var showSuccessNotify = function() {
$.notify({
message: '操作成功'
},{
type: 'success'
});
}
// lock user
$('.users-list-container').on('click', '.lock-action', function(){
var $lockAction = $(this);
var $unlockAction = $lockAction.siblings('.unlock-action');
var userId = $lockAction.data('id');
$.ajax({
url: '/admins/users/' + userId + '/lock',
method: 'POST',
dataType: 'json',
success: function() {
showSuccessNotify();
$lockAction.hide();
$unlockAction.show();
}
});
});
// unlock user
$('.users-list-container').on('click', '.unlock-action', function(){
var $unlockAction = $(this);
var $lockAction = $unlockAction.siblings('.lock-action');
var userId = $unlockAction.data('id');
$.ajax({
url: '/admins/users/' + userId + '/unlock',
method: 'POST',
dataType: 'json',
success: function() {
showSuccessNotify();
$lockAction.show();
$unlockAction.hide();
}
});
});
// active user
$('.users-list-container').on('click', '.active-action', function(){
var $activeAction = $(this);
var $unlockAction = $activeAction.siblings('.unlock-action');
var $lockAction = $activeAction.siblings('.lock-action');
var userId = $activeAction.data('id');
$.ajax({
url: '/admins/users/' + userId + '/unlock',
method: 'POST',
dataType: 'json',
success: function() {
showSuccessNotify();
$activeAction.hide();
$lockAction.show();
$unlockAction.hide();
}
});
});
// ***************** reward grade modal *****************
var $rewardGradeModal = $('.admin-users-reward-grade-modal');
var $form = $rewardGradeModal.find('form.admin-users-reward-grade-form');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
grade: {
required: true,
digits: true
},
}
});
// modal ready fire
$rewardGradeModal.on('show.bs.modal', function (event) {
var $link = $(event.relatedTarget);
var userId = $link.data('id');
$rewardGradeModal.find('.modal-body input[name="user_id"]').val(userId);
});
// modal visited fire
$rewardGradeModal.on('shown.bs.modal', function(){
$rewardGradeModal.find('.modal-body input[name="grade"]').focus();
});
$('.admin-users-reward-grade-modal .submit-btn').on('click', function(){
$form.find('.error').html('');
if ($form.valid()) {
var userId = $form.find('input[name="user_id"]').val();
$.ajax({
method: 'POST',
dataType: 'json',
url: "/admins/users/" + userId + "/reward_grade",
data: $form.serialize(),
success: function(data) {
showSuccessNotify();
$('.users-list-container .user-item-' + userId + ' td.grade-content').html(data.grade);
$rewardGradeModal.modal('hide');
},
error: function(res) {
$rewardGradeModal.find('.error').html(res.responseJSON.message);
}
});
}
});
}
});

@ -13,4 +13,7 @@
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require_tree .

File diff suppressed because it is too large Load Diff

@ -0,0 +1,350 @@
/*
* Project: Bootstrap Notify = v3.1.3
* Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
* Author: Mouse0270 aka Robert McIntosh
* License: MIT License
* Website: https://github.com/mouse0270/bootstrap-growl
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
// Create the defaults once
var defaults = {
element: 'body',
position: null,
type: "info",
allow_dismiss: true,
newest_on_top: false,
showProgressbar: false,
placement: {
from: "top",
align: "right"
},
offset: 20,
spacing: 10,
z_index: 1031,
delay: 1000,
timer: 1000,
url_target: '_blank',
mouse_over: null,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onShow: null,
onShown: null,
onClose: null,
onClosed: null,
icon_type: 'class',
template: '<div data-notify="container" class="col-xs-4 col-sm-2 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">&times;</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
};
String.format = function() {
var str = arguments[0];
for (var i = 1; i < arguments.length; i++) {
str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
}
return str;
};
function Notify ( element, content, options ) {
// Setup Content of Notify
var content = {
content: {
message: typeof content == 'object' ? content.message : content,
title: content.title ? content.title : '',
icon: content.icon ? content.icon : '',
url: content.url ? content.url : '#',
target: content.target ? content.target : '-'
}
};
options = $.extend(true, {}, content, options);
this.settings = $.extend(true, {}, defaults, options);
this._defaults = defaults;
if (this.settings.content.target == "-") {
this.settings.content.target = this.settings.url_target;
}
this.animations = {
start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
}
if (typeof this.settings.offset == 'number') {
this.settings.offset = {
x: this.settings.offset,
y: this.settings.offset
};
}
this.init();
};
$.extend(Notify.prototype, {
init: function () {
var self = this;
this.buildNotify();
if (this.settings.content.icon) {
this.setIcon();
}
if (this.settings.content.url != "#") {
this.styleURL();
}
this.placement();
this.bind();
this.notify = {
$ele: this.$ele,
update: function(command, update) {
var commands = {};
if (typeof command == "string") {
commands[command] = update;
}else{
commands = command;
}
for (var command in commands) {
switch (command) {
case "type":
this.$ele.removeClass('alert-' + self.settings.type);
this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
self.settings.type = commands[command];
this.$ele.addClass('alert-' + commands[command]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[command]);
break;
case "icon":
var $icon = this.$ele.find('[data-notify="icon"]');
if (self.settings.icon_type.toLowerCase() == 'class') {
$icon.removeClass(self.settings.content.icon).addClass(commands[command]);
}else{
if (!$icon.is('img')) {
$icon.find('img');
}
$icon.attr('src', commands[command]);
}
break;
case "progress":
var newDelay = self.settings.delay - (self.settings.delay * (commands[command] / 100));
this.$ele.data('notify-delay', newDelay);
this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[command]).css('width', commands[command] + '%');
break;
case "url":
this.$ele.find('[data-notify="url"]').attr('href', commands[command]);
break;
case "target":
this.$ele.find('[data-notify="url"]').attr('target', commands[command]);
break;
default:
this.$ele.find('[data-notify="' + command +'"]').html(commands[command]);
};
}
var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
self.reposition(posX);
},
close: function() {
self.close();
}
};
},
buildNotify: function () {
var content = this.settings.content;
this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
if (!this.settings.allow_dismiss) {
this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
}
if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
this.$ele.find('[data-notify="progressbar"]').remove();
}
},
setIcon: function() {
if (this.settings.icon_type.toLowerCase() == 'class') {
this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
}else{
if (this.$ele.find('[data-notify="icon"]').is('img')) {
this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
}else{
this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />');
}
}
},
styleURL: function() {
this.$ele.find('[data-notify="url"]').css({
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
height: '100%',
left: '0px',
position: 'absolute',
top: '0px',
width: '100%',
zIndex: this.settings.z_index + 1
});
this.$ele.find('[data-notify="dismiss"]').css({
position: 'absolute',
right: '10px',
top: '5px',
zIndex: this.settings.z_index + 2
});
},
placement: function() {
var self = this,
offsetAmt = this.settings.offset.y,
css = {
display: 'inline-block',
margin: '0px auto',
position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
transition: 'all .5s ease-in-out',
zIndex: this.settings.z_index
},
hasAnimation = false,
settings = this.settings;
$('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() {
return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
});
if (this.settings.newest_on_top == true) {
offsetAmt = this.settings.offset.y;
}
css[this.settings.placement.from] = offsetAmt+'px';
switch (this.settings.placement.align) {
case "left":
case "right":
css[this.settings.placement.align] = this.settings.offset.x+'px';
break;
case "center":
css.left = 0;
css.right = 0;
break;
}
this.$ele.css(css).addClass(this.settings.animate.enter);
$.each(Array('webkit', 'moz', 'o', 'ms', ''), function(index, prefix) {
self.$ele[0].style[prefix+'AnimationIterationCount'] = 1;
});
$(this.settings.element).append(this.$ele);
if (this.settings.newest_on_top == true) {
offsetAmt = (parseInt(offsetAmt)+parseInt(this.settings.spacing)) + this.$ele.outerHeight();
this.reposition(offsetAmt);
}
if ($.isFunction(self.settings.onShow)) {
self.settings.onShow.call(this.$ele);
}
this.$ele.one(this.animations.start, function(event) {
hasAnimation = true;
}).one(this.animations.end, function(event) {
if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this);
}
});
setTimeout(function() {
if (!hasAnimation) {
if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this);
}
}
}, 600);
},
bind: function() {
var self = this;
this.$ele.find('[data-notify="dismiss"]').on('click', function() {
self.close();
})
this.$ele.mouseover(function(e) {
$(this).data('data-hover', "true");
}).mouseout(function(e) {
$(this).data('data-hover', "false");
});
this.$ele.data('data-hover', "false");
if (this.settings.delay > 0) {
self.$ele.data('notify-delay', self.settings.delay);
var timer = setInterval(function() {
var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over == "pause") || self.settings.mouse_over != "pause") {
var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
self.$ele.data('notify-delay', delay);
self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
}
if (delay <= -(self.settings.timer)) {
clearInterval(timer);
self.close();
}
}, self.settings.timer);
}
},
close: function() {
var self = this,
$successors = null,
posX = parseInt(this.$ele.css(this.settings.placement.from)),
hasAnimation = false;
this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
self.reposition(posX);
if ($.isFunction(self.settings.onClose)) {
self.settings.onClose.call(this.$ele);
}
this.$ele.one(this.animations.start, function(event) {
hasAnimation = true;
}).one(this.animations.end, function(event) {
$(this).remove();
if ($.isFunction(self.settings.onClosed)) {
self.settings.onClosed.call(this);
}
});
setTimeout(function() {
if (!hasAnimation) {
self.$ele.remove();
if (self.settings.onClosed) {
self.settings.onClosed(self.$ele);
}
}
}, 600);
},
reposition: function(posX) {
var self = this,
notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
$elements = this.$ele.nextAll(notifies);
if (this.settings.newest_on_top == true) {
$elements = this.$ele.prevAll(notifies);
}
$elements.each(function() {
$(this).css(self.settings.placement.from, posX);
posX = (parseInt(posX)+parseInt(self.settings.spacing)) + $(this).outerHeight();
});
}
});
$.notify = function ( content, options ) {
var plugin = new Notify( this, content, options );
return plugin.notify;
};
$.notifyDefaults = function( options ) {
defaults = $.extend(true, {}, defaults, options);
return defaults;
};
$.notifyClose = function( command ) {
if (typeof command === "undefined" || command == "all") {
$('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
}else{
$('[data-notify-position="'+command+'"]').find('[data-notify="dismiss"]').trigger('click');
}
};
}));

File diff suppressed because one or more lines are too long

@ -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);

@ -0,0 +1,33 @@
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
define( ["jquery", "../jquery.validate"], factory );
} else {
factory( jQuery );
}
}(function( $ ) {
/*
* Translated default messages for the jQuery validation plugin.
* Locale: ZH (Chinese, 中文 (Zhōngwén), 汉语, 漢語)
*/
$.extend($.validator.messages, {
required: "这是必填字段",
remote: "请修正此字段",
email: "请输入有效的电子邮件地址",
url: "请输入有效的网址",
date: "请输入有效的日期",
dateISO: "请输入有效的日期 (YYYY-MM-DD)",
number: "请输入有效的数字",
digits: "只能输入数字",
creditcard: "请输入有效的信用卡号码",
equalTo: "你的输入不相同",
extension: "请输入有效的后缀",
maxlength: $.validator.format("最多可以输入 {0} 个字符"),
minlength: $.validator.format("最少要输入 {0} 个字符"),
rangelength: $.validator.format("请输入长度在 {0} 到 {1} 之间的字符串"),
range: $.validator.format("请输入范围在 {0} 到 {1} 之间的数值"),
max: $.validator.format("请输入不大于 {0} 的数值"),
min: $.validator.format("请输入不小于 {0} 的数值")
});
}));

@ -0,0 +1,3 @@
/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(n){return"请删除"+(n.input.length-n.maximum)+"个字符"},inputTooShort:function(n){return"请再输入至少"+(n.minimum-n.input.length)+"个字符"},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(n){return"最多只能选择"+n.maximum+"个项目"},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"},removeAllItems:function(){return"删除所有项目"}}}),n.define,n.require}();

@ -0,0 +1,2 @@
/*! jquery.cookie v1.4.1 | MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});

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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,46 @@
@import "bootstrap";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "select2.min";
@import "select2-bootstrap4.min";
@import "bootstrap-datepicker";
@import "bootstrap-datepicker.standalone";
@import "admins/*";
body {
width: 100vw;
height: 100vh;
max-width: 100vw;
max-height: 100vh;
display: flex;
align-items: stretch;
font-size: 14px;
background: #efefef;
}
a {
&:hover {
text-decoration: unset;
}
}
input.danger {
border-color: #dc3545!important;
}
label.error {
color: #dc3545!important;
}
.simple_form {
.form-group {
.collection_radio_buttons {
margin-bottom: 0px;
}
.form-check-inline {
height: calc(1.5em + 0.75rem + 2px)
}
}
}

@ -0,0 +1,104 @@
.admin-body-container {
padding: 20px;
flex: 1;
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-y: scroll;
& > .content {
flex: 1;
font-size: 14px;
.box {
padding: 20px;
border-radius: 5px;
background: #fff;
}
}
/* 面包屑 */
.breadcrumb {
padding-left: 5px;
font-size: 20px;
background: unset;
}
/* 内容表格 */
table {
table-layout: fixed;
td {
vertical-align: middle;
}
tr {
&.no-data {
&:hover {
color: darkgrey;
background: unset;
}
& > td {
text-align: center;
height: 300px;
}
}
}
}
.action-container {
.action {
padding: 0 3px;
}
}
/* 分页 */
.paginate-container {
margin-top: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.paginate-total {
margin-bottom: 10px;
color: darkgrey;
}
.pagination {
margin-bottom: 0px;
}
}
/* 搜索表单 */
.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,215 @@
#sidebar {
min-width: 200px;
max-width: 200px;
background: #272822;
color: #fff;
transition: all 0.5s;
overflow-y: scroll;
&::-webkit-scrollbar {
display:none
}
&.active {
min-width: 60px;
max-width: 60px;
text-align: center;
.sidebar-header {
padding: 10px;
display: flex;
flex-direction: column;
&-logo {
padding-left: 5px;
overflow: hidden;
margin-bottom: 10px;
}
}
ul li a {
padding: 10px;
text-align: center;
font-size: 0.85em;
display: flex;
justify-content: center;
span { display: none }
i {
margin-right: 0;
display: block;
font-size: 1.8em;
margin-bottom: 5px;
width: 30px;
height: 20px;
}
}
.dropdown-toggle::after {
top: auto;
bottom: 10px;
right: 50%;
-webkit-transform: translateX(50%);
-ms-transform: translateX(50%);
transform: translateX(50%);
}
ul ul a {
padding: 10px !important;
span { display: none }
i {
margin-left: 0px;
display: block;
font-size: 0.8em;
width: 30px;
height: 10px;
}
}
}
.sidebar-header {
padding: 20px;
background: #272822;
display: flex;
flex-direction: row;
justify-content: space-between;
}
#sidebarCollapse {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
text-align: right;
&.active {
width: 40px;
height: 30px;
background: #3f3f3f;
border: 1px solid grey;
border-radius: 3px;
i.fold { display: none; }
i.unfold { display: block; }
}
i.fold {
display: block;
}
i.unfold { display: none; }
}
a, a:hover, a:focus {
color: inherit;
text-decoration: none;
transition: all 0.3s;
}
& > ul > li > a > i {
width: 14px;
height: 14px;
}
ul {
&.components {
padding: 20px 0;
border-bottom: 1px solid #3f3f3f;
}
p {
color: #fff;
padding: 10px;
}
li > a {
padding: 10px;
font-size: 1em;
display: block;
text-align: left;
i {
margin-right: 10px;
font-size: 1em;
margin-bottom: 5px;
}
}
li a {
&:hover, &.active {
color: #fff;
background: #276891;
}
}
li.active > a, a[aria-expanded="true"] {
color: #fff;
//background: #276891;
}
ul a {
font-size: 0.9em !important;
padding-left: 30px !important;
background: #3f3f3f;
}
}
}
@media (max-width: 768px) {
#sidebar {
&.active {
padding: 10px 5px;
min-width: 40px;
max-width: 40px;
text-align: center;
margin-left: 0;
transform: none;
.sidebar-header {
padding: 0px;
.sidebar-header-logo {
display: none;
}
#sidebarCollapse {
width: 30px;
height: 20px;
}
}
ul li a {
padding: 10px;
font-size: 0.85em;
i {
margin-right: 0;
display: block;
margin-bottom: 5px;
}
}
& > ul > li > a > i {
font-size: 1.8em;
}
ul ul a {
padding: 10px !important;
}
}
.sidebar-header {
}
}
.dropdown-toggle::after {
top: auto;
bottom: 10px;
right: 50%;
-webkit-transform: translateX(50%);
-ms-transform: translateX(50%);
transform: translateX(50%);
}
}

@ -0,0 +1,36 @@
.admins-users-index-page {
.user-list-form {
}
.users-list-container {
text-align: center;
}
}
.admins-users-edit-page, .admins-users-update-page {
.user-edit-container {
.user-info {
&-content {
padding-top: 5px;
padding-bottom: 5px;
height: 80px;
}
&-name {
flex: 2;
font-size: 16px;
}
&-auth {
flex: 1;
i.fa {
margin-right: 10px;
font-size: 16px;
width: 16px;
height: 16px;
text-align: center;
}
}
}
}
}

@ -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 @@
.select2-container--bootstrap4 .select2-selection--single{height:calc(1.5em + .75rem + 2px)!important}.select2-container--bootstrap4 .select2-selection--single .select2-selection__placeholder{color:#757575;line-height:calc(1.5em + .75rem)}.select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow{position:absolute;top:50%;right:3px;width:20px}.select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow b{top:60%;border-color:#343a40 transparent transparent;border-style:solid;border-width:5px 4px 0;width:0;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute}.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered{line-height:calc(1.5em + .75rem)}.select2-search--dropdown .select2-search__field{border:1px solid #ced4da;border-radius:.25rem}.select2-results__message{color:#6c757d}.select2-container--bootstrap4 .select2-selection--multiple{min-height:calc(1.5em + .75rem + 2px)!important}.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__rendered{-webkit-box-sizing:border-box;box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice{color:#343a40;border:1px solid #bdc6d0;border-radius:.2rem;padding:0 5px 0 0;cursor:pointer;float:left;margin-top:.3em;margin-right:5px}.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice__remove{color:#bdc6d0;font-weight:700;margin-left:3px;margin-right:1px;padding-right:3px;padding-left:3px;float:left}.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice__remove:hover{color:#343a40}.select2-container{display:block}.select2-container :focus{outline:0}.input-group .select2-container--bootstrap4{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.input-group-prepend~.select2-container--bootstrap4 .select2-selection{border-top-left-radius:0;border-bottom-left-radius:0}.select2-container--bootstrap4 .select2-selection{border:1px solid #ced4da;border-radius:.25rem;width:100%}.select2-container--bootstrap4.select2-container--focus .select2-selection{border-color:#17a2b8;-webkit-box-shadow:0 0 0 .2rem rgba(0,123,255,.25);box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.select2-container--bootstrap4.select2-container--focus.select2-container--open .select2-selection{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--bootstrap4.select2-container--disabled.select2-container--focus .select2-selection,.select2-container--bootstrap4.select2-container--disabled .select2-selection{background-color:#e9ecef;cursor:not-allowed;border-color:#ced4da;-webkit-box-shadow:none;box-shadow:none}.select2-container--bootstrap4.select2-container--disabled.select2-container--focus .select2-search__field,.select2-container--bootstrap4.select2-container--disabled .select2-search__field{background-color:transparent}form.was-validated select:invalid~.select2-container--bootstrap4 .select2-selection,select.is-invalid~.select2-container--bootstrap4 .select2-selection{border-color:#dc3545}form.was-validated select:valid~.select2-container--bootstrap4 .select2-selection,select.is-valid~.select2-container--bootstrap4 .select2-selection{border-color:#28a745}.select2-container--bootstrap4 .select2-dropdown{border-color:#ced4da;border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--bootstrap4 .select2-dropdown.select2-dropdown--above{border-top:1px solid #ced4da;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.select2-container--bootstrap4 .select2-dropdown .select2-results__option[aria-selected=true]{background-color:#e9ecef}.select2-container--bootstrap4 .select2-results__option--highlighted,.select2-container--bootstrap4 .select2-results__option--highlighted.select2-results__option[aria-selected=true]{background-color:#007bff;color:#f8f9fa}.select2-container--bootstrap4 .select2-results__option[role=group]{padding:0}.select2-container--bootstrap4 .select2-results>.select2-results__options{max-height:15em;overflow-y:auto}.select2-container--bootstrap4 .select2-results__group{padding:6px;display:list-item;color:#6c757d}.select2-container--bootstrap4 .select2-selection__clear{width:1.2em;height:1.2em;line-height:1.15em;padding-left:.3em;margin-top:.5em;border-radius:100%;background-color:#6c757d;color:#f8f9fa;float:right;margin-right:.3em}.select2-container--bootstrap4 .select2-selection__clear:hover{background-color:#343a40}

File diff suppressed because one or more lines are too long

@ -1,3 +0,0 @@
// Place all the styles related to the users/banks controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -1,8 +0,0 @@
class AdminController < ApplicationController
layout 'admin'
before_action :require_admin
def index
end
end

@ -0,0 +1,24 @@
class Admins::BaseController < ApplicationController
include Admins::PaginateHelper
include Admins::RenderHelper
include Admins::ErrorRescueHandler
layout 'admin'
before_action :require_login, :require_admin!
private
def require_login
return if User.current.logged?
redirect_to "/login?back_url=#{CGI::escape(request.fullpath)}"
end
def require_admin!
return if current_user.blank? || !current_user.logged?
return if current_user.admin_or_business?
render_forbidden
end
end

@ -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,4 @@
class Admins::DashboardsController < Admins::BaseController
def index
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

@ -0,0 +1,62 @@
class Admins::UsersController < Admins::BaseController
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
users = Admins::UserQuery.call(params)
@users = paginate users.includes(user_extension: :school)
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
Admins::UpdateUserService.call(@user, update_params)
flash[:success] = '保存成功'
redirect_to edit_admins_user_path(@user)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'edit'
rescue Admins::UpdateUserService::Error => ex
flash.now[:danger] = ex.message
render 'edit'
end
def destroy
User.find(params[:id]).destroy!
render_delete_success
end
def lock
User.find(params[:user_id]).lock!
render_ok
end
def unlock
User.find(params[:user_id]).activate!
render_ok
end
def reward_grade
user = User.find(params[:user_id])
return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0
RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true)
render_ok(grade: user.grade)
end
private
def update_params
params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id
mail phone location location_city school_id department_id admin business is_test
password professional_certification authentication])
end
end

@ -239,7 +239,7 @@ class ApplicationController < ActionController::Base
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
if !User.current.logged? && Rails.env.development?
User.current = User.find 1
User.current = User.find 57703
end

@ -0,0 +1,24 @@
module Admins::ErrorRescueHandler
extend ActiveSupport::Concern
included do
rescue_from Exception, Educoder::TipException do |e|
raise e if Rails.env.development?
Util.logger_error e
internal_server_error
end
rescue_from ActionView::MissingTemplate, ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::ParameterMissing do
render_unprocessable_entity('参数缺失')
end
# form validation error
rescue_from ActiveModel::ValidationError do |ex|
render_unprocessable_entity(ex.model.errors.full_messages.join(','))
end
rescue_from ActiveRecord::RecordInvalid do |ex|
render_unprocessable_entity(ex.record.errors.full_messages.join(','))
end
end
end

@ -0,0 +1,24 @@
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
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, 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

@ -0,0 +1,47 @@
module Admins::RenderHelper
extend ActiveSupport::Concern
def render_forbidden
respond_to do |format|
format.html { redirect_to '/403' }
format.json { super }
end
end
def render_not_found
respond_to do |format|
format.html { render 'admins/shared/404' }
format.js { render_js_error('资源未找到') }
format.json { render status: 404, json: { message: '资源未找到' } }
end
end
def render_unprocessable_entity(message)
respond_to do |format|
format.html { render 'admins/shared/422' }
format.js { render_js_error(message) }
format.json { render status: 422, json: { message: message } }
end
end
alias_method :render_error, :render_unprocessable_entity
def internal_server_error
respond_to do |format|
format.html { render 'admins/shared/500' }
format.js { render_js_error(message) }
format.json { render status: 500, json: { message: '系统错误' } }
end
end
def render_js_template(template, **opts)
render({ template: template, formats: :js }.merge(opts))
end
def render_delete_success
render_js_template 'admins/shared/delete'
end
def render_js_error(message)
render_js_template 'admins/shared/error', locals: { message: message }
end
end

@ -386,6 +386,92 @@ 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
content_tag(:span, opts) do
link_to path, remote: true do
content += content_tag(:i, '', class: "fa color-light-green ml-1 #{arrow_class}") 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

@ -0,0 +1,11 @@
class SidebarUtil
class << self
def controller_name(name)
sidebar_controller_map[name]
end
def sidebar_controller_map
@_sidebar_controller_map ||= YAML.load_file(Rails.root.join('config/admins', 'sidebar.yml'))
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

@ -146,6 +146,8 @@ class User < ApplicationRecord
attr_accessor :password, :password_confirmation
delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true
before_save :update_hashed_password
#
@ -232,8 +234,9 @@ class User < ApplicationRecord
user_extension&.school&.name || ''
end
def school_id
user_extension&.school_id
# 用户的学院名称
def department_name
user_extension&.department&.name || ''
end
# 课堂的老师(创建者、老师、助教)
@ -440,6 +443,10 @@ class User < ApplicationRecord
name.gsub(/\s+/, '').strip #6.11 -hs
end
def only_real_name
"#{lastname}#{firstname}"
end
# 用户是否选题毕设课题
def selected_topic?(topic)
student_graduation_topics.where(graduation_topic_id: topic.id).last.try(:status)

@ -0,0 +1,40 @@
class Admins::UserQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :created_on, :last_login_on, :experience, :grade, default_by: :created_on, default_direction: :desc
def initialize(params)
@params = params
end
def call
users = User.where(type: 'User')
# 状态
status = params[:status]
users = users.where(status: status) if status.present?
# 职业
users = users.joins(:user_extension).where(user_extensions: { identity: params[:identity] }) if params[:identity].present?
# 授权类型
if params[:auto_trial].present?
users = users.joins(user_extension: :school).where(schools: { auto_users_trial: params[:auto_trial].to_i == 1 })
end
# 关键字检索
keyword = params[:keyword].to_s.strip.presence
if keyword
sql = 'CONCAT(lastname, firstname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword'
users = users.where(sql, keyword: keyword)
end
# 学校名称
school_name = params[:school_name].to_s.strip.presence
users = users.joins(user_extension: :school).where('schools.name LIKE ?', "%#{school_name}%") if school_name
custom_sort(users, params[:sort_by], params[:sort_direction])
end
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,52 @@
class Admins::UpdateUserService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params
end
def call
user.assign_attributes(user_attributes)
user.firstname = ''
user.password = password if params[:password].present?
if params[:identity].to_s == 'student'
params[:technical_title] = nil
else
params[:student_id] = nil
end
user.user_extension.assign_attributes(user_extension_attributes)
ActiveRecord::Base.transaction do
user.save!
user.user_extension.save!
update_gitlab_password if params[:password].present?
end
user
end
private
def user_attributes
params.slice(*%i[lastname nickname mail phone admin business is_test
professional_certification authentication])
end
def user_extension_attributes
params.slice(*%i[gender identity technical_title student_id location location_city school_id department_id])
end
def update_gitlab_password
return if user.gid.blank?
# 同步修改gitlab密码
Gitlab.client.edit_user(user.gid, password: params[:password])
rescue Exception => ex
Util.logger_error(ex)
raise Error, '保存失败'
end
end

@ -1,13 +1,14 @@
class RewardGradeService < ApplicationService
attr_reader :user, :attrs
attr_reader :user, :attrs, :not_unique
def initialize(user, **attrs)
@user = user
@not_unique = attrs.delete(:not_unique) || false
@attrs = attrs.slice(*%i[container_id container_type score])
end
def call
return if user.grades.exists?(attrs)
return if user.grades.exists?(attrs) && !not_unique
ActiveRecord::Base.transaction do
grade = user.grades.create!(attrs)

@ -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

@ -1,619 +0,0 @@
2222222222z
<!-- Start right content -->
<div class="content-page">
<!-- ============================================================== -->
<!-- Start Content here -->
<!-- ============================================================== -->
<div class="content">
<!-- Start info box -->
<div class="row top-summary">
<div class="col-lg-3 col-md-6">
<div class="widget green-1 animated fadeInDown">
<div class="widget-content padding">
<div class="widget-icon">
<i class="icon-globe-inv"></i>
</div>
<div class="text-box">
<p class="maindata">TOTAL <b>VISITORS</b></p>
<h2><span class="animate-number" data-value="25153" data-duration="3000">0</span></h2>
<div class="clearfix"></div>
</div>
</div>
<div class="widget-footer">
<div class="row">
<div class="col-sm-12">
<i class="fa fa-caret-up rel-change"></i> <b>39%</b> increase in traffic
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="widget darkblue-2 animated fadeInDown">
<div class="widget-content padding">
<div class="widget-icon">
<i class="icon-bag"></i>
</div>
<div class="text-box">
<p class="maindata">TOTAL <b>SALES</b></p>
<h2><span class="animate-number" data-value="6399" data-duration="3000">0</span></h2>
<div class="clearfix"></div>
</div>
</div>
<div class="widget-footer">
<div class="row">
<div class="col-sm-12">
<i class="fa fa-caret-down rel-change"></i> <b>11%</b> decrease in sales
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="widget orange-4 animated fadeInDown">
<div class="widget-content padding">
<div class="widget-icon">
<i class="fa fa-dollar"></i>
</div>
<div class="text-box">
<p class="maindata">OVERALL <b>INCOME</b></p>
<h2>$<span class="animate-number" data-value="70389" data-duration="3000">0</span></h2>
<div class="clearfix"></div>
</div>
</div>
<div class="widget-footer">
<div class="row">
<div class="col-sm-12">
<i class="fa fa-caret-down rel-change"></i> <b>7%</b> decrease in income
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="widget lightblue-1 animated fadeInDown">
<div class="widget-content padding">
<div class="widget-icon">
<i class="fa fa-users"></i>
</div>
<div class="text-box">
<p class="maindata">TOTAL <b>USERS</b></p>
<h2><span class="animate-number" data-value="18648" data-duration="3000">0</span></h2>
<div class="clearfix"></div>
</div>
</div>
<div class="widget-footer">
<div class="row">
<div class="col-sm-12">
<i class="fa fa-caret-up rel-change"></i> <b>6%</b> increase in users
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<!-- End of info box -->
<div class="row">
<div class="col-lg-8 portlets">
<div id="website-statistics1" class="widget">
<div class="widget-header transparent">
<h2><i class="icon-chart-line"></i> <strong>Website</strong> Statistics</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a class="hidden" id="dropdownMenu1" data-toggle="dropdown">
<i class="fa fa-cogs"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content">
<div id="website-statistic" class="statistic-chart">
<div class="row stacked">
<div class="col-sm-12">
<div class="toolbar">
<div class="pull-left">
<div class="btn-group">
<a href="#" class="btn btn-default btn-xs">Daily</a>
<a href="#" class="btn btn-default btn-xs active">Monthly</a>
<a href="#" class="btn btn-default btn-xs">Yearly</a>
</div>
</div>
<div class="pull-right">
<div class="btn-group">
<a class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Export <i class="icon-down-open-2"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="#">Export as PDF</a></li>
<li><a href="#">Export as CSV</a></li>
<li><a href="#">Export as PNG</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</div>
<a href="#" class="btn btn-primary btn-xs"><i class="icon-cog-2"></i></a>
</div>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
<div id="morris-home" class="morris-chart" style="height: 270px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 portlets">
<div class="widget darkblue-3">
<div class="widget-header transparent">
<h2><strong>Server</strong> Status</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a class="hidden" id="dropdownMenu1" data-toggle="dropdown">
<i class="fa fa-cogs"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content">
<div id="website-statistic2" class="statistic-chart">
<div class="col-sm-12 stacked">
<h4><i class="fa fa-circle-o text-green-1"></i> Server Loads</h4>
<div class="col-sm-8 status-data">
<div class="col-xs-12">
<div class="row stacked">
<div class="col-xs-4 text-center right-border">
Processes<br>
<span class="animate-number" data-value="322" data-duration="3000">0</span>
</div>
<div class="col-xs-4 text-center right-border">
Connections<br>
<span class="animate-number" data-value="4789" data-duration="3000">0</span>
</div>
<div class="col-xs-4 text-center">
Avg. Load<br>
<span class="animate-number" data-value="76" data-duration="3000">0</span>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="progress progress-xs">
<div style="width: 72%" aria-valuemax="100" aria-valuemin="0" aria-valuenow="72" role="progressbar" class="progress-bar bg-orange-2" title="Average Load: 76%" data-placement="right" data-toggle="tooltip">
<span class="sr-only">72% Complete (success)</span>
</div>
</div>
</div>
<div class="col-sm-4 text-center">
<div class="ws-load echart" data-percent="50"><span class="percent"></span></div>
</div>
</div>
<div class="clearfix"></div>
<div id="home-chart-2"></div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8 portlets">
<div class="widget">
<div class="widget-header">
<h2><i class="icon-chart-pie-1"></i> <strong>Sales</strong> Report</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a class="hidden" id="dropdownMenu1" data-toggle="dropdown">
<i class="fa fa-cogs"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content">
<div class="row stacked">
<div class="col-sm-5 mini-stats">
<div id="morris-bar-home" class="morris-chart" style="height: 170px;"></div>
<div class="sales-report-data">
<span class="pull-left">Completed Sales</span><span class="pull-right">65 / 174</span>
<div class="progress progress-xs">
<div style="width: 38%;" class="progress-bar bg-blue-1"></div>
</div>
<div class="clearfix"></div>
<span class="pull-left">Return(s) Processed</span><span class="pull-right">22 / 25</span>
<div class="progress progress-xs">
<div style="width: 88%;" class="progress-bar bg-lightblue-1"></div>
</div>
<div class="clearfix"></div>
<span class="pull-left">Shipped Products</span><span class="pull-right">418 / 624</span>
<div class="progress progress-xs">
<div style="width: 58%;" class="progress-bar"></div>
</div>
<div class="clearfix"></div>
<span class="pull-left">Overall Product Stock</span><span class="pull-right">19%</span>
<div class="progress progress-xs">
<div style="width: 19%;" class="progress-bar bg-pink-1"></div>
</div>
</div>
</div>
<div class="col-sm-7">
<div id="vector-map" style="height:370px"></div>
</div>
</div>
<div class="clearfix"></div>
<div id="sales-report" class="collapse in hidden-xs">
<div class="table-responsive">
<table data-sortable class="table table-striped">
<thead>
<tr><th width="70">No</th><th data-sortable="false" width="40"><input type="checkbox" id="select-all-rows"></th><th width="120">Order ID</th><th>Buyer</th><th width="100">Status</th><th width="150">Location</th><th width="120">Total</th></tr>
</thead>
<tbody>
<tr><td>1</td><td><input type="checkbox" class="rows-check"></td><td>#0021</td><td><a href="#">John Doe</a></td><td><span class="label label-primary">Order</span></td><td>Yogyakarta, ID</td><td><strong class="text-primary">&#36; 1,245</strong></td></tr>
<tr><td>2</td><td><input type="checkbox" class="rows-check"></td><td>#0022</td><td><a href="#">Johnny Depp</a></td><td><span class="label label-success">Payment</span></td><td>London, UK</td><td><strong class="text-success">&#36; 1,245</strong></td></tr>
<tr><td>3</td><td><input type="checkbox" class="rows-check"></td><td>#0023</td><td><a href="#">Scarlett Johansson</a></td><td><span class="label label-success">Payment</span></td><td>Canbera, AU</td><td><strong class="text-success">&#36; 1,245</strong></td></tr>
<tr><td>4</td><td><input type="checkbox" class="rows-check"></td><td>#0024</td><td><a href="#">Hanna Barbara</a></td><td><span class="label label-danger">Cancel</span></td><td>Bali, ID</td><td><strong class="text-danger">&#36; 1,245</strong></td></tr>
<tr><td>5</td><td><input type="checkbox" class="rows-check"></td><td>#0025</td><td><a href="#">Ali Larter</a></td><td><span class="label label-primary">Order</span></td><td>Bandung, ID</td><td><strong class="text-primary">&#36; 1,245</strong></td></tr>
<tr><td>6</td><td><input type="checkbox" class="rows-check"></td><td>#0026</td><td><a href="#">Willy Wonka</a></td><td><span class="label label-danger">Cancel</span></td><td>Semarang, ID</td><td><strong class="text-danger">&#36; 1,245</strong></td></tr>
<tr><td>7</td><td><input type="checkbox" class="rows-check"></td><td>#0027</td><td><a href="#">Chris Isaac</a></td><td><span class="label label-warning">Waiting</span></td><td>New York, US</td><td><strong class="text-warning">&#36; 1,245</strong></td></tr>
<tr><td>8</td><td><input type="checkbox" class="rows-check"></td><td>#0028</td><td><a href="#">Jenny Doe</a></td><td><span class="label label-primary">Order</span></td><td>Boston, US</td><td><strong class="text-primary">&#36; 1,245</strong></td></tr>
<tr><td>9</td><td><input type="checkbox" class="rows-check"></td><td>#0029</td><td><a href="#">Ban ki moon</a></td><td><span class="label label-danger">Cancel</span></td><td>Boston, US</td><td><strong class="text-danger">&#36; 1,245</strong></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 portlets">
<div class="row">
<div class="col-sm-12">
<div id="todo-app" class="widget">
<div class="widget-header centered">
<div class="left-btn"><a class="btn btn-sm btn-default add-todo"><i class="fa fa-plus"></i></a></div>
<h2>Todo List</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content padding-sm">
<ul class="todo-list">
<li>
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Generate monthly sales report for John</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
<span class="todo-tags pull-right">
<div class="label label-success">New</div>
</span>
</li>
<li class="high">
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Mail those reports to John</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
</li>
<li>
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Don't forget to send those reports to John</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
</li>
<li class="medium">
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">If you forgot, go back to office to pick them up</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
<span class="todo-tags pull-right">
<div class="label label-info">Today</div>
</span>
</li>
<li class="low">
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Deliver reports by hand to John</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
</li>
<li>
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Say John that you are sorry</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
</li>
<li>
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Beg for your job...</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
<span class="todo-tags pull-right">
<div class="label label-danger">Important</div>
</span>
</li>
<li>
<span class="check-icon"><input type="checkbox" /></span>
<span class="todo-item">Look for a new job</span>
<span class="todo-options pull-right">
<a href="javascript:;" class="todo-delete"><i class="icon-cancel-3"></i></a>
</span>
<span class="todo-tags pull-right">
<div class="label label-warning"><i class="icon-search"></i></div>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id="notes-app" class="widget">
<div class="notes-line"></div>
<div class="widget-header centered transparent">
<div class="left-btn btn-group"><a class="btn btn-sm btn-primary add-note"><i class="fa fa-plus"></i></a><a class="btn btn-sm btn-primary back-note-list"><i class="icon-align-justify"></i></a></div>
<h2>Notes</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content padding-sm">
<div id="notes-list">
<div class="scroller">
<ul class="list-unstyled">
</ul>
</div>
</div>
<div id="note-data">
<form>
<textarea class="form-control" id="note-text" placeholder="Your note..."></textarea>
</form>
</div>
<div class="status-indicator">Saved</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 portlets">
<div id="weather-widget" class="widget">
<div class="widget-header transparent">
<h2><strong>Weather</strong> Widget</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a class="hidden" id="dropdownMenu1" data-toggle="dropdown">
<i class="fa fa-cogs"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div id="weather" class="widget-content">
</div><i class="wi-day-rain-mix"></i>
<button class="js-geolocation btn btn-sm btn-default" style="display: none;">Use Your Location</button>
</div>
</div>
<div class="col-lg-4 col-md-6 portlets">
<div id="calendar-widget2" class="widget blue-1">
<div class="widget-header transparent">
<h2><strong>Calendar</strong> Widget</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div id="calendar-box2" class="widget-content col-sm-12">
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 portlets">
<div id="calc" class="widget darkblue-2">
<div class="widget-header">
<div class="additional-btn left-toolbar">
<div class="btn-group">
<a class="additional-icon" id="dropdownMenu2" data-toggle="dropdown">
Calculator <i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2">
<li><a href="#">Save</a></li>
<li><a href="#">Export</a></li>
<li class="divider"></li>
<li><a href="#">Quit</a></li>
</ul>
</div>
</div>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div id="calculator" class="widget-content">
<div class="calc-top col-xs-12">
<div class="row">
<div class="col-xs-3"><span class="calc-clean">C</span></div>
<div class="col-xs-9"><div class="calc-screen"></div></div>
</div>
</div>
<div class="calc-keys col-xs-12">
<div class="row">
<div class="col-xs-3"><span>7</span></div>
<div class="col-xs-3"><span>8</span></div>
<div class="col-xs-3"><span>9</span></div>
<div class="col-xs-3"><span class="calc-operator">+</span></div>
</div>
<div class="row">
<div class="col-xs-3"><span>4</span></div>
<div class="col-xs-3"><span>5</span></div>
<div class="col-xs-3"><span>6</span></div>
<div class="col-xs-3"><span class="calc-operator">-</span></div>
</div>
<div class="row">
<div class="col-xs-3"><span>1</span></div>
<div class="col-xs-3"><span>2</span></div>
<div class="col-xs-3"><span>3</span></div>
<div class="col-xs-3"><span class="calc-operator">÷</span></div>
</div>
<div class="row">
<div class="col-xs-3"><span>0</span></div>
<div class="col-xs-3"><span>.</span></div>
<div class="col-xs-3"><span class="calc-eval">=</span></div>
<div class="col-xs-3"><span class="calc-operator">x</span></div>
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8 col-md-6 portlets">
</div>
<div class="col-lg-4 col-md-6 portlets">
<div id="stock-app" class="widget green-3">
<div class="widget-header transparent">
<h2><strong>Stock</strong> Markets</h2>
<div class="additional-btn">
<a href="#" class="hidden reload"><i class="icon-ccw-1"></i></a>
<a class="hidden" id="dropdownMenu1" data-toggle="dropdown">
<i class="fa fa-cogs"></i>
</a>
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
<a href="#" class="widget-popout hidden tt" title="Pop Out/In"><i class="icon-publish"></i></a>
<a href="#" class="widget-maximize hidden"><i class="icon-resize-full-1"></i></a>
<a href="#" class="widget-toggle"><i class="icon-down-open-2"></i></a>
<a href="#" class="widget-close"><i class="icon-cancel-3"></i></a>
</div>
</div>
<div class="widget-content">
<div id="website-statistic3" class="statistic-chart">
<div class="row">
<div class="col-xs-6">
<h2>NASDAQ</h2>
</div>
<div class="col-xs-6">
<label id="nasdaq-status" class="stock-status"></label>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<h2>DOW JONES</h2>
</div>
<div class="col-xs-6">
<label id="dow-status" class="stock-status"></label>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<h2>S&P</h2>
</div>
<div class="col-xs-6">
<label id="sp-status" class="stock-status"></label>
</div>
</div>
</div>
<div id="home-chart-3"></div>
</div>
<div class="widget-footer">
</div>
</div>
</div>
</div>
<!-- Footer Start -->
<footer>
Huban Creative &copy; 2014
<div class="footer-links pull-right">
<a href="#">About</a><a href="#">Support</a><a href="#">Terms of Service</a><a href="#">Legal</a><a href="#">Help</a><a href="#">Contact Us</a>
</div>
</footer>
<!-- Footer End -->
</div>
<!-- ============================================================== -->
<!-- End content here -->
<!-- ============================================================== -->
</div>
<!-- End right content -->

@ -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,188 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('概览', admins_path) %>
<% end %>
<div class="header bg-gradient-primary pb-8 pt-md-8">
<div class="container-fluid">
<div class="header-body">
<!-- Card stats -->
<div class="row">
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">Traffic</h5>
<span class="h2 font-weight-bold mb-0">350,897</span>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-danger text-white rounded-circle shadow">
<i class="fas fa-pie-chart"></i>
</div>
</div>
</div>
<p class="mt-3 mb-0 text-muted text-sm">
<span class="text-success mr-2"><i class="fa fa-arrow-up"></i> 3.48%</span>
<span class="text-nowrap">Since last month</span>
</p>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">New users</h5>
<span class="h2 font-weight-bold mb-0">2,356</span>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-warning text-white rounded-circle shadow">
<i class="fas fa-pie-chart"></i>
</div>
</div>
</div>
<p class="mt-3 mb-0 text-muted text-sm">
<span class="text-danger mr-2"><i class="fas fa-arrow-down"></i> 3.48%</span>
<span class="text-nowrap">Since last week</span>
</p>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">Sales</h5>
<span class="h2 font-weight-bold mb-0">924</span>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-yellow text-white rounded-circle shadow">
<i class="fas fa-user"></i>
</div>
</div>
</div>
<p class="mt-3 mb-0 text-muted text-sm">
<span class="text-warning mr-2"><i class="fas fa-arrow-down"></i> 1.10%</span>
<span class="text-nowrap">Since yesterday</span>
</p>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">Performance</h5>
<span class="h2 font-weight-bold mb-0">49,65%</span>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-info text-white rounded-circle shadow">
<i class="fas fa-pie-chart"></i>
</div>
</div>
</div>
<p class="mt-3 mb-0 text-muted text-sm">
<span class="text-success mr-2"><i class="fas fa-arrow-up"></i> 12%</span>
<span class="text-nowrap">Since last month</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid mt--7">
<div class="row mt-5">
<div class="col-xl-8 mb-5 mb-xl-0">
<div class="card shadow">
<div class="card-header border-0">
<div class="row align-items-center">
<div class="col">
<h3 class="mb-0">Page visits</h3>
</div>
<div class="col text-right">
<a href="#!" class="btn btn-sm btn-primary">Test</a>
</div>
</div>
</div>
<div class="table-responsive">
<!-- Projects table -->
<table class="table align-items-center table-flush">
<thead class="thead-light">
<tr>
<th scope="col">Test</th>
<th scope="col">Test</th>
<th scope="col">Test</th>
<th scope="col">Test</th>
</tr>
</thead>
<tbody>
<% 5.times do %>
<tr>
<th scope="row">/test/</th>
<td>4,569</td>
<td>340</td>
<td>
<i class="fas fa-arrow-up text-success mr-3"></i> 46,53%
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-xl-4">
<div class="card shadow">
<div class="card-header border-0">
<div class="row align-items-center">
<div class="col">
<h3 class="mb-0">Test</h3>
</div>
<div class="col text-right">
<a href="#!" class="btn btn-sm btn-primary">Test</a>
</div>
</div>
</div>
<div class="table-responsive">
<!-- Projects table -->
<table class="table align-items-center table-flush">
<thead class="thead-light">
<tr>
<th scope="col">Test</th>
<th scope="col">Test</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% 5.times do %>
<tr>
<th scope="row">
Test
</th>
<td>
1,480
</td>
<td>
<div class="d-flex align-items-center">
<span class="mr-2">60%</span>
<div>
<div class="progress">
<div class="progress-bar bg-gradient-danger" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;"></div>
</div>
</div>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,11 @@
<%# Link to the "First" page
- available local variables
url: url to the first page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item first-page">
<%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote, class: 'page-link' %>
</li>

@ -0,0 +1,13 @@
<%# Non-link tag that stands for skipped pages...
- available local variables
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item">
<%= link_to 'javascript:void(0)', { class: 'page-link' } do %>
<%= t('views.pagination.truncate').html_safe %>
<span class="sr-only">(current)</span>
<% end %>
</li>

@ -0,0 +1,11 @@
<%# Link to the "Last" page
- available local variables
url: url to the last page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item last-page">
<%= link_to_unless(current_page.last?, t('views.pagination.last').html_safe, url, remote: remote, class: 'page-link') %>
<li class="page-item last-page">

@ -0,0 +1,11 @@
<%# Link to the "Next" page
- available local variables
url: url to the next page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item next">
<%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote, class: 'page-link' %>
</li>

@ -0,0 +1,19 @@
<%# Link showing page number
- available local variables
page: a page object for "this" page
url: url to this page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item page<%= ' current active' if page.current? %>">
<% if page.current? %>
<%= link_to url, {remote: remote, rel: page.rel, class: 'page-link'} do %>
<%= page %>
<span class="sr-only">(current)</span>
<% end %>
<% else %>
<%= link_to page, url, {remote: remote, rel: page.rel, class: 'page-link'} %>
<% end %>
</li>

@ -0,0 +1,27 @@
<%# The container tag
- available local variables
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
paginator: the paginator that renders the pagination tags inside
-%>
<%= paginator.render do -%>
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<%= first_page_tag unless current_page.first? %>
<%= prev_page_tag unless current_page.first? %>
<% each_page do |page| -%>
<% if page.display_tag? -%>
<%= page_tag page %>
<% elsif !page.was_truncated? -%>
<%= gap_tag %>
<% end -%>
<% end -%>
<% unless current_page.out_of_range? %>
<%= next_page_tag unless current_page.last? %>
<%= last_page_tag unless current_page.last? %>
<% end %>
</ul>
</nav>
<% end -%>

@ -0,0 +1,11 @@
<%# Link to the "Previous" page
- available local variables
url: url to the previous page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<li class="page-item prev">
<%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote, class: 'page-link' %>
</li>

@ -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 } %>

@ -0,0 +1,6 @@
<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="global-error-text">资源未找到</div>
</div>

@ -0,0 +1,6 @@
<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="global-error-text"><%= @message %></div>
</div>

@ -0,0 +1,6 @@
<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="global-error-text">系统错误</div>
</div>

@ -0,0 +1,6 @@
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<%= message %>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>

@ -0,0 +1,13 @@
<% if @_admin_breadcrumbs.present? %>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<% @_admin_breadcrumbs&.each_with_index do |item, index| %>
<% if item.url.present? && index != @_admin_breadcrumbs.size - 1 %>
<li class="breadcrumb-item"><%= link_to item.text, item.url %></li>
<% else %>
<li class="breadcrumb-item active" aria-current="page"><%= item.text %></li>
<% end %>
<% end %>
</ol>
</nav>
<% end %>

@ -0,0 +1,20 @@
<% flash.each do |k, v| %>
<% next unless %w(success danger warning info).include?(k.to_s) %>
<div class="alert alert-<%= k %> alert-dismissible fade show" role="alert">
<%= v %>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<% end %>
<% flash.now.as_json.each do |k, v| %>
<% next unless %w(success danger warning info).include?(k.to_s) %>
<div class="alert alert-<%= k %> alert-dismissible fade show" role="alert">
<%= v %>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<% end %>

@ -0,0 +1 @@
<tr class="no-data"><td colspan="100">暂无数据</td></tr>

@ -0,0 +1,6 @@
<div class="paginate-container">
<% if objects.size.nonzero? %>
<div class="paginate-total"><%= page_entries_info objects %></div>
<% end %>
<%= paginate objects, views_prefix: 'admins', remote: true %>
</div>

@ -0,0 +1,42 @@
<% sidebar_collapse = request.cookies['admin_sidebar_collapse'].to_s == 'true' %>
<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') %>
</a>
<div id="sidebarCollapse" class="navbar-btn <%= sidebar_collapse ? 'active' : '' %>">
<i class="fa fa-chevron-left fold" data-toggle="tooltip" data-placement="right" data-boundary="window" title="收起"></i>
<i class="fa fa-bars unfold" data-toggle="tooltip" data-placement="right" data-boundary="window" title="展开"></i>
</div>
</div>
<!-- Sidebar Links -->
<ul class="list-unstyled components">
<li><%= sidebar_item(admins_path, '概览', icon: 'dashboard', controller: 'admins-dashboards') %></li>
<li>
<%= sidebar_item_group('#school-submenu', '学校统计', icon: 'area-chart') do %>
<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>
<!-- <li>-->
<%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
<!-- <li><%#= sidebar_item('#', '课程列表', icon: 'calendar', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '课堂列表', icon: 'book', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '实训作业', icon: 'file-code-o', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '项目列表', icon: 'sitemap', controller: '') %></li>-->
<%# end %>
<!-- </li>-->
<li>
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li>
<!-- <li><%#= sidebar_item('#', '试用授权列表', icon: 'id-card-o', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '自动授权列表', icon: 'id-card', controller: '') %></li>-->
<% end %>
</li>
<li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li>
</ul>
</nav>

@ -0,0 +1,20 @@
var deleteRow = $('<%= params[:element] %>');
var refreshUrl = '<%= params[:refresh_url] %>';
var refreshFunc = function(url) {
$.ajax({
url: url.length > 0 ? url : window.location.href,
method: 'GET',
dataType: "script"
})
}
if(deleteRow.length > 0){
var needRefresh = deleteRow.siblings().length == 0;
deleteRow.remove();
if(needRefresh){ refreshFunc(refreshUrl); }
} else {
refreshFunc(refreshUrl);
}

@ -0,0 +1,7 @@
$('.admin-alert-container').html('<%= j( render partial: 'admins/shared/alert', locals: { message: message } ) %>');
setTimeout(function() {
if ($('.admin-alert-container button.close').length > 0) {
$('.admin-alert-container button.close').trigger('click');
}
}, 2000)

@ -0,0 +1,137 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('用户管理', admins_users_path)
add_admin_breadcrumb('用户详情')
end
%>
<div class="box user-edit-container">
<div class="user-info mb-4 row">
<%= link_to "/users/#{@user.login}", class: 'user-info-avatar col-md-1', target: '_blank', data: { toggle: 'tooltip', title: '个人中心' } do %>
<img src="/images/<%= url_to_avatar(@user) %>" class="rounded-circle" width="80" height="80" />
<% end %>
<div class="d-flex flex-column justify-content-between col-md-3 user-info-content">
<div class="user-info-name flex"><%= @user.real_name %> | <%= @user.id %> | <%= @user.login %></div>
<div class="d-flex flex-row user-info-auth">
<% if @user.authentication? %>
<i class="fa fa-user text-success" data-toggle="tooltip" data-placement="bottom" title="已实名认证"></i>
<% elsif @user.process_real_name_apply.present? %>
<i class="fa fa-user text-danger" data-toggle="tooltip" data-placement="bottom" title="实名认证中"></i>
<% else %>
<i class="fa fa-user text-muted" data-toggle="tooltip" data-placement="bottom" title="未实名认证"></i>
<% end %>
<% if @user.professional_certification %>
<i class="fa fa-list-alt text-success" data-toggle="tooltip" data-placement="bottom" title="已职业认证"></i>
<% elsif @user.process_professional_apply.present? %>
<i class="fa fa-list-alt text-danger" data-toggle="tooltip" data-placement="bottom" title="职业认证中"></i>
<% else %>
<i class="fa fa-list-alt text-muted" data-toggle="tooltip" data-placement="bottom" title="未职业认证"></i>
<% end %>
<% if @user.phone.present? %>
<i class="fa fa-mobile text-success" data-toggle="tooltip" data-placement="bottom" title="已绑定手机"></i>
<% else %>
<i class="fa fa-mobile text-muted" data-toggle="tooltip" data-placement="bottom" title="未绑定手机"></i>
<% end %>
<% if @user.mail.present? %>
<i class="fa fa-envelope text-success" data-toggle="tooltip" data-placement="bottom" title="已绑定邮箱"></i>
<% else %>
<i class="fa fa-envelope text-muted" data-toggle="tooltip" data-placement="bottom" title="未绑定邮箱"></i>
<% end %>
</div>
<div class="user-info-last-login">最近登录:<%= @user.last_login_on&.strftime('%Y-%m-%d %H:%M') %></div>
</div>
</div>
<%= simple_form_for(@user, url: admins_user_path(@user)) do |f| %>
<div><h6>基本信息</h6></div>
<div class="form-group px-2">
<div class="form-row">
<%= f.input :lastname, label: '姓名', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-md-11', value: @user.only_real_name } %>
</div>
<div class="form-row">
<%= f.input :nickname, label: '昵称', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-md-11' } %>
<%= f.input :gender, as: :radio_buttons, label: '性别', collection: [%w(男 0), %w(女 1)], wrapper_html: { class: 'col-md-3' } %>
</div>
<div class="form-row user-identity-select">
<div class="form-group select optional col-md-1">
<%= f.label :identity, label: '职业' %>
<%= select_tag('user[identity]', [], class: 'form-control identity-select optional', 'data-value': @user.user_extension&.identity, 'data-first-title': '请选择') %>
</div>
<div class="form-group technical-title-select-wrapper optional col-md-1" style="<%= @user.user_extension.student? ? 'display:none;' : '' %>">
<%= f.label :technical_title, label: '职称' %>
<%= select_tag('user[technical_title]', [], class: 'form-control technical-title-select optional', 'data-value': @user.technical_title) %>
</div>
<%= f.input :student_id, as: :tel, label: '学号', wrapper_html: { class: 'col-md-2', style: @user.user_extension.student? ? '' : 'display:none;' }, input_html: { class: 'student-id-input' } %>
</div>
<div class="form-row">
<%= f.input :mail, as: :email, label: '邮箱地址', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11' } %>
<%= f.input :phone, as: :tel, label: '手机号', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11' } %>
</div>
<div class="form-row province-city-select">
<div class="form-group select optional col-md-2">
<%= f.label :location, label: '省份' %>
<%= select_tag('user[location]', [], class: 'form-control province-select optional', 'data-value': @user.location, 'data-first-title': '请选择') %>
</div>
<div class="form-group select optional col-md-2">
<%= f.label :location_city, label: '城市' %>
<%= select_tag('user[location_city]', [], class: 'form-control city-select optional', 'data-value': @user.location_city) %>
</div>
</div>
<div class="form-row">
<%= f.input :school_id, as: :hidden %>
<%= f.input :department_id, as: :hidden %>
<div class="form-group select optional col-md-2">
<%= f.label :school_name, label: '所属学校/单位' %>
<%= f.select :school_name, [@user.school_id], {}, class: 'form-control school-select optional' %>
</div>
<div class="form-group select optional col-md-2">
<%= f.label :department_name, label: '所属学院/部门' %>
<%= f.select :department_name, [@user.department_id], {}, class: 'form-control department-select optional' %>
</div>
</div>
</div>
<div class="mt-4"><h6>管理</h6></div>
<div class="form-group px-2">
<% if current_user.admin? %>
<div class="form-group check_boxes optional">
<%= f.label :role, label: '角色' %>
<div class="d-flex">
<%= f.input :admin, as: :boolean, label: '管理员', checked_value: 1, unchecked_value: 0 %>
<%= f.input :business, as: :boolean, label: '运营人员', wrapper_html: { class: 'ml-3' }, checked_value: 1, unchecked_value: 0 %>
<%= f.input :is_test, as: :boolean, label: '测试账号', wrapper_html: { class: 'ml-3' }, checked_value: 1, unchecked_value: 0 %>
</div>
</div>
<% end %>
<div class="form-group check_boxes optional">
<%= f.label :role, label: '认证信息' %>
<div class="d-flex">
<%= f.input :professional_certification, as: :boolean, label: '职业认证', checked_value: 1, unchecked_value: 0 %>
<%= f.input :authentication, as: :boolean, label: '实名认证', wrapper_html: { class: 'ml-3' }, checked_value: 1, unchecked_value: 0 %>
</div>
</div>
<div class="form-row">
<%= f.input :password, as: :password, label: '修改密码', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11' } %>
<%= f.input :password_confirmation, as: :password, label: '确认密码', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11' } %>
</div>
</div>
<div class="form-row mt-4">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %>
<%= link_to '取消', 'javascript:history.go(-1)', class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,35 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('用户管理', admins_users_path) %>
<% end %>
<div class="box search-form-container user-list-form">
<%= form_tag(admins_users_path, method: :get, class: 'form-inline search-form', remote: true) do %>
<div class="form-group mr-2">
<label for="status">状态:</label>
<% status_options = [['全部', ''], ['正常', User::STATUS_ACTIVE], ['未激活', User::STATUS_REGISTERED], ['已锁定', User::STATUS_LOCKED]] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<div class="form-group mr-2">
<label for="identity">职业:</label>
<% identity_options = [['全部', '']] + UserExtension.identities.map { |k, v| [I18n.t("user.identity.#{k}"), v] } %>
<%= select_tag(:identity, options_for_select(identity_options), class: 'form-control') %>
</div>
<div class="form-group mr-2">
<label for="identity">授权类型:</label>
<% auto_trial_options = [['全部', ''], ['自动授权', 1], ['手动授权', 0]] %>
<%= select_tag(:auto_trial, options_for_select(auto_trial_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'ID/姓名/邮箱/手机号检索') %>
<%= text_field_tag(:school_name, params[:school_name], class: 'form-control col-sm-2', placeholder: '学校/单位检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
</div>
<div class="box users-list-container">
<%= render partial: 'admins/users/shared/user_list', locals: { users: @users } %>
</div>
<%= render partial: 'admins/users/shared/reward_grade_modal' %>

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

@ -0,0 +1,26 @@
<div class="modal fade admin-users-reward-grade-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">奖励金币</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-users-reward-grade-form">
<%= hidden_field_tag(:user_id, nil) %>
<div class="form-group">
<label for="grade" class="col-form-label">金币数量:</label>
<%= number_field_tag(:grade, nil, class: 'form-control') %>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,60 @@
<table class="table table-hover users-list-table">
<thead class="thead-light">
<tr>
<th width="10%">ID</th>
<th width="10%">真实姓名</th>
<th width="16%">邮件地址</th>
<th width="10%">手机号码</th>
<th width="14%">单位</th>
<th width="12%"><%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %></th>
<th width="12%"><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %></th>
<th width="10%"><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %></th>
<th width="8%"><%= sort_tag('金币', name: 'grade', path: admins_users_path) %></th>
<th width="14%">操作</th>
</tr>
</thead>
<tbody>
<% if users.present? %>
<% users.each do |user| %>
<tr class="user-item-<%= user.id %>">
<td>
<%= link_to "/users/#{user.login}", target: '_blank' do %>
<%= overflow_hidden_span user.login, width: 100 %>
<% end %>
</td>
<td>
<%= link_to edit_admins_user_path(user) do %>
<%= overflow_hidden_span user.real_name, width: 100 %>
<% end %>
</td>
<td><%= overflow_hidden_span display_text(user.mail), width: 150 %></td>
<td><%= overflow_hidden_span display_text(user.phone), width: 100 %></td>
<td><%= overflow_hidden_span display_text(user.school_name), width: 150 %></td>
<td><%= display_text(user.created_on&.strftime('%Y-%m-%d %H:%M')) %></td>
<td><%= display_text(user.last_login_on&.strftime('%Y-%m-%d %H:%M')) %></td>
<td><%= user.experience.to_i %></td>
<td class="grade-content"><%= user.grade.to_i %></td>
<td class="action-container">
<%= 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;' %>
<% if user.registered? %>
<%= javascript_void_link '激活', class: 'action active-action', data: { id: user.id, confirm: '确认激活吗?' } %>
<% end %>
<% if user.id != current_user.id %>
<%= javascript_void_link '加锁', class: 'action lock-action', data: { id: user.id, confirm: '确认加锁吗?' }, style: user.locked? || user.registered? ? 'display: none;' : '' %>
<% end %>
<%= delete_link '删除', admins_user_path(user, element: ".user-item-#{user.id}"), class: 'delete-user-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: users } %>

@ -0,0 +1,6 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('用户管理', admins_users_path) %>
<% add_admin_breadcrumb('用户详情') %>
<% end %>
<h3>Users Show</h3>

@ -1,522 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>EduCoder后台管理</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<head>
<title>EduCoder后台管理</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
</head>
<%= stylesheet_link_tag 'admin', media: 'all','data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'admin', 'data-turbolinks-track': 'reload' %>
</head>
<body class="fixed-left">
<!-- Modal Start -->
<!-- Modal Task Progress -->
<div class="md-modal md-3d-flip-vertical" id="task-progress">
<div class="md-content">
<h3><strong>Task Progress</strong> Information</h3>
<div>
<p>CLEANING BUGS</p>
<div class="progress progress-xs for-modal">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%">
<span class="sr-only">80&#37; Complete</span>
</div>
</div>
<p>POSTING SOME STUFF</p>
<div class="progress progress-xs for-modal">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 65%">
<span class="sr-only">65&#37; Complete</span>
</div>
</div>
<p>BACKUP DATA FROM SERVER</p>
<div class="progress progress-xs for-modal">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 95%">
<span class="sr-only">95&#37; Complete</span>
</div>
</div>
<p>RE-DESIGNING WEB APPLICATION</p>
<div class="progress progress-xs for-modal">
<div class="progress-bar progress-bar-primary" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 100%">
<span class="sr-only">100&#37; Complete</span>
</div>
</div>
<p class="text-center">
<button class="btn btn-danger btn-sm md-close">Close</button>
</p>
</div>
</div>
</div>
<!-- Modal Logout -->
<div class="md-modal md-just-me" id="logout-modal">
<div class="md-content">
<h3><strong>Logout</strong> Confirmation</h3>
<div>
<p class="text-center">Are you sure want to logout from this awesome system?</p>
<p class="text-center">
<button class="btn btn-danger md-close">Nope!</button>
<a href="login.html" class="btn btn-success md-close">Yeah, I'm sure</a>
</p>
</div>
</div>
</div> <!-- Modal End -->
<!-- Begin page -->
<div id="wrapper">
<% body_class = [params[:controller].gsub(/\//, '-').gsub('_', '-'), params[:action], 'page'].join('-') %>
<body class="<%= body_class %>">
<!-- Sidebar -->
<%= render partial: 'admins/shared/sidebar' %>
<!-- Top Bar Start -->
<div class="topbar">
<div class="topbar-left">
<div class="logo">
<h1><a href="#"><img src="admin/img/logo.png" alt="Logo"></a></h1>
<!-- Page Content -->
<div class="admin-body-container">
<div class="admin-alert-container">
<%= render 'admins/shared/flash_notice' %>
</div>
<button class="button-menu-mobile open-left">
<i class="fa fa-bars"></i>
</button>
</div>
<!-- Button mobile view to collapse sidebar menu -->
<div class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-collapse2">
<ul class="nav navbar-nav hidden-xs">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-th"></i></a>
<div class="dropdown-menu grid-dropdown">
<div class="row stacked">
<div class="col-xs-4">
<a href="javascript:;" data-app="notes-app" data-status="active"><i class="icon-edit"></i>Notes</a>
</div>
<div class="col-xs-4">
<a href="javascript:;" data-app="todo-app" data-status="active"><i class="icon-check"></i>Todo List</a>
</div>
<div class="col-xs-4">
<a href="javascript:;" data-app="calc" data-status="inactive"><i class="fa fa-calculator"></i>Calculator</a>
</div>
</div>
<div class="row stacked">
<div class="col-xs-4">
<a href="javascript:;" data-app="weather-widget" data-status="active"><i class="icon-cloud-3"></i>Weather</a>
</div>
<div class="col-xs-4">
<a href="javascript:;" data-app="calendar-widget2" data-status="active"><i class="icon-calendar"></i>Calendar</a>
</div>
<div class="col-xs-4">
<a href="javascript:;" data-app="stock-app" data-status="inactive"><i class="icon-chart-line"></i>Stocks</a>
</div>
</div>
<div class="clearfix"></div>
</div>
</li>
<li class="language_bar dropdown hidden-xs">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">English (US) <i class="fa fa-caret-down"></i></a>
<ul class="dropdown-menu pull-right">
<li><a href="#">German</a></li>
<li><a href="#">French</a></li>
<li><a href="#">Italian</a></li>
<li><a href="#">Spanish</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right top-navbar">
<li class="dropdown iconify hide-phone">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-globe"></i><span class="label label-danger absolute">4</span></a>
<ul class="dropdown-menu dropdown-message">
<li class="dropdown-header notif-header"><i class="icon-bell-2"></i> New Notifications<a class="pull-right" href="#"><i class="fa fa-cog"></i></a></li>
<li class="unread">
<a href="#">
<p><strong>John Doe</strong> Uploaded a photo <strong>&#34;DSC000254.jpg&#34;</strong>
<br /><i>2 minutes ago</i>
</p>
</a>
</li>
<li class="unread">
<a href="#">
<p><strong>John Doe</strong> Created an photo album <strong>&#34;Fappening&#34;</strong>
<br /><i>8 minutes ago</i>
</p>
</a>
</li>
<li>
<a href="#">
<p><strong>John Malkovich</strong> Added 3 products
<br /><i>3 hours ago</i>
</p>
</a>
</li>
<li>
<a href="#">
<p><strong>Sonata Arctica</strong> Send you a message <strong>&#34;Lorem ipsum dolor...&#34;</strong>
<br /><i>12 hours ago</i>
</p>
</a>
</li>
<li>
<a href="#">
<p><strong>Johnny Depp</strong> Updated his avatar
<br /><i>Yesterday</i>
</p>
</a>
</li>
<li class="dropdown-footer">
<div class="btn-group btn-group-justified">
<div class="btn-group">
<a href="#" class="btn btn-sm btn-primary"><i class="icon-ccw-1"></i> Refresh</a>
</div>
<div class="btn-group">
<a href="#" class="btn btn-sm btn-danger"><i class="icon-trash-3"></i> Clear All</a>
</div>
<div class="btn-group">
<a href="#" class="btn btn-sm btn-success">See All <i class="icon-right-open-2"></i></a>
</div>
</div>
</li>
</ul>
</li>
<li class="dropdown iconify hide-phone">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-envelope"></i><span class="label label-danger absolute">3</span></a>
<ul class="dropdown-menu dropdown-message">
<li class="dropdown-header notif-header"><i class="icon-mail-2"></i> New Messages</li>
<li class="unread">
<a href="#" class="clearfix">
<img src="admin/images/users/chat/2.jpg" class="xs-avatar ava-dropdown" alt="Avatar">
<strong>John Doe</strong><i class="pull-right msg-time">5 minutes ago</i><br />
<p>Duis autem vel eum iriure dolor in hendrerit ...</p>
</a>
</li>
<li class="unread">
<a href="#" class="clearfix">
<img src="admin/images/users/chat/1.jpg" class="xs-avatar ava-dropdown" alt="Avatar">
<strong>Sandra Kraken</strong><i class="pull-right msg-time">22 minutes ago</i><br />
<p>Duis autem vel eum iriure dolor in hendrerit ...</p>
</a>
</li>
<li>
<a href="#" class="clearfix">
<img src="admin/images/users/chat/3.jpg" class="xs-avatar ava-dropdown" alt="Avatar">
<strong>Zoey Lombardo</strong><i class="pull-right msg-time">41 minutes ago</i><br />
<p>Duis autem vel eum iriure dolor in hendrerit ...</p>
</a>
</li>
<li class="dropdown-footer"><div class=""><a href="#" class="btn btn-sm btn-block btn-primary"><i class="fa fa-share"></i> See all messages</a></div></li>
</ul>
</li>
<li class="dropdown iconify hide-phone"><a href="#" onclick="javascript:toggle_fullscreen()"><i class="icon-resize-full-2"></i></a></li>
<li class="dropdown topbar-profile">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="rounded-image topbar-profile-image"><img src="admin/images/users/user-35.jpg"></span> Jane <strong>Doe</strong> <i class="fa fa-caret-down"></i></a>
<ul class="dropdown-menu">
<li><a href="#">My Profile</a></li>
<li><a href="#">Change Password</a></li>
<li><a href="#">Account Setting</a></li>
<li class="divider"></li>
<li><a href="#"><i class="icon-help-2"></i> Help</a></li>
<li><a href="lockscreen.html"><i class="icon-lock-1"></i> Lock me</a></li>
<li><a class="md-trigger" data-modal="logout-modal"><i class="icon-logout-1"></i> Logout</a></li>
</ul>
</li>
<li class="right-opener">
<a href="javascript:;" class="open-right"><i class="fa fa-angle-double-left"></i><i class="fa fa-angle-double-right"></i></a>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<!-- Top Bar End -->
<!-- Left Sidebar Start -->
<div class="left side-menu">
<div class="sidebar-inner slimscrollleft">
<!-- Search form -->
<form role="search" class="navbar-form">
<div class="form-group">
<input type="text" placeholder="Search" class="form-control">
<button type="submit" class="btn search-button"><i class="fa fa-search"></i></button>
</div>
</form>
<div class="clearfix"></div>
<!--- Profile -->
<div class="profile-info">
<div class="col-xs-4">
<a href="profile.html" class="rounded-image profile-image"><img src="admin/images/users/user-100.jpg"></a>
</div>
<div class="col-xs-8">
<div class="profile-text">Welcome <b>Jane</b></div>
<div class="profile-buttons">
<a href="javascript:;"><i class="fa fa-envelope-o pulse"></i></a>
<a href="#connect" class="open-right"><i class="fa fa-comments"></i></a>
<a href="javascript:;" title="Sign Out"><i class="fa fa-power-off text-red-1"></i></a>
</div>
</div>
</div>
<!--- Divider -->
<div class="clearfix"></div>
<hr class="divider" />
<div class="clearfix"></div>
<!--- Divider -->
<div id="sidebar-menu">
<ul><li class='has_sub'><a href='javascript:void(0);'><i class='icon-home-3'></i><span>Dashboard</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='index.html' class='active'><span>Dashboard v1</span></a></li><li><a href='index2.html'><span>Dashboard v2</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='icon-feather'></i><span>UI Elements</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='alerts.html'><span>Alerts</span></a></li><li><a href='buttons.html'><span>Buttons</span></a></li><li><a href='calendar.html'><span>Calendar</span></a></li><li><a href='grid.html'><span>Grid</span></a></li><li><a href='icons.html'><span>Icons</span></a></li><li><a href='modals.html'><span>Modals</span></a></li><li><a href='nested-list.html'><span>Nested List</span></a></li><li><a href='notifications.html'><span>Notifications</span></a></li><li><a href='portlets.html'><span>Portlets</span></a></li><li><a href='progress-bars.html'><span>Progress Bars</span></a></li><li><a href='tabs-accordions.html'><span>Tabs & Accordions</span></a></li><li><a href='typography.html'><span>Typography</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='icon-pencil-3'></i><span>Forms</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='forms.html'><span>Form Elements</span></a></li><li><a href='advanced-forms.html'><span>Advanced Forms</span></a></li><li><a href='form-wizard.html'><span>Form Wizard</span></a></li><li><a href='form-validation.html'><span>Form Validation</span></a></li><li><a href='form-uploads.html'><span>File Uploads</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='fa fa-table'></i><span>Tables</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='tables.html'><span>Basic Tables</span></a></li><li><a href='datatables.html'><span>Datatables</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='fa fa-map-marker'></i><span>Maps</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='google-maps.html'><span>Google Maps</span></a></li><li><a href='vector-maps.html'><span>Vector Maps</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='fa fa-envelope'></i><span>Email</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='inbox.html'><span>Inbox</span></a></li><li><a href='read-message.html'><span>View Email</span></a></li><li><a href='new-message.html'><span>New Message</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='icon-chart-line'></i><span>Charts</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='sparkline-charts.html'><span>Sparkline Charts</span></a></li><li><a href='morris-charts.html'><span>Morris Charts</span></a></li><li><a href='rickshaw-charts.html'><span>Rickshaw Charts</span></a></li><li><a href='other-charts.html'><span>Other Charts</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><i class='icon-megaphone'></i><span>Extras</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='blank.html'><span>Blank Page</span></a></li><li><a href='login.html'><span>Login</span></a></li><li><a href='register.html'><span>Register</span></a></li><li><a href='lockscreen.html'><span>Lock Screen</span></a></li><li><a href='404.html'><span>404 Error</span></a></li><li><a href='500.html'><span>500 Error</span></a></li><li><a href='profile.html'><span>User Profile</span></a></li><li><a href='invoice.html'><span>Invoice</span></a></li><li><a href='gallery.html'><span>Gallery</span></a></li><li><a href='maintenance.html'><span>Maintenance</span></a></li><li class='has_sub'><a href='javascript:void(0);'><span>3 Level menu</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='javascript:void(0);'><span>Sub Item</span></a></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><span>4 Level Menu</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li class='has_sub'><a href='javascript:void(0);'><span>Sub Item - level 3</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='javascript:void(0);'><span>Sub Item - level 4</span></a></li></ul></li></ul></li><li class='has_sub'><a href='javascript:void(0);'><span>Submenu with icons</span> <span class="pull-right"><i class="fa fa-angle-down"></i></span></a><ul><li><a href='javascript:void(0);'><i class='fa fa-camera'></i><span>Item with icon</span></a></li><li><a href='javascript:void(0);'><i class='entypo entypo-users'></i><span>Another Item</span></a></li></ul></li></ul></li></ul> <div class="clearfix"></div>
</div>
<div class="clearfix"></div>
<div class="portlets">
<div id="chat_groups" class="widget transparent nomargin">
<h2>Chat Groups</h2>
<div class="widget-content">
<ul class="list-unstyled">
<li><a href="javascript:;"><i class="fa fa-circle-o text-red-1"></i> Colleagues</a></li>
<li><a href="javascript:;"><i class="fa fa-circle-o text-blue-1"></i> Family</a></li>
<li><a href="javascript:;"><i class="fa fa-circle-o text-green-1"></i> Friends</a></li>
</ul>
</div>
</div>
<div id="recent_tickets" class="widget transparent nomargin">
<h2>Recent Tickets</h2>
<div class="widget-content">
<ul class="list-unstyled">
<li>
<a href="javascript:;">My wordpress blog is broken <span>I was trying to save my page and...</span></a>
</li>
<li>
<a href="javascript:;">Server down, need help!<span>My server is not responding for the last...</span></a>
</li>
</ul>
</div>
</div>
<div class="breadcrumb-container">
<%= yield :setup_admin_breadcrumb %>
<%= render(partial: 'admins/shared/breadcrumb') %>
</div>
<div class="clearfix"></div><br><br><br>
</div>
<div class="left-footer">
<div class="progress progress-xs">
<div class="progress-bar bg-green-1" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%">
<span class="progress-precentage">80%</span>
</div>
<a data-toggle="tooltip" title="See task progress" class="btn btn-default md-trigger" data-modal="task-progress"><i class="fa fa-inbox"></i></a>
<div class="content">
<%= yield %>
</div>
</div>
</div>
<!-- Left Sidebar End --> <!-- Right Sidebar Start -->
<div class="right side-menu">
<ul class="nav nav-tabs nav-justified" id="right-tabs">
<li class="active"><a href="#feed" data-toggle="tab" title="Live Feed"><i class="icon-rss-2"></i></a></li>
<li><a href="#connect" data-toggle="tab" title="Chat"><i class="icon-chat"></i></a></li>
<li><a href="#settings" data-toggle="tab" title="Preferences"><i class="icon-wrench"></i></a></li>
</ul>
<div class="clearfix"></div>
<div class="tab-content">
<div class="tab-pane active" id="feed">
<div class="tab-inner slimscroller">
<div class="search-right">
<input type="text" class="form-control" placeholder="Search">
</div>
<div class="right-toolbar">
<a href="javascript:;" class="pull-right">Settings <i class="icon-cog"></i></a>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
<div class="panel-group" id="collapse">
<div class="panel panel-default">
<div class="panel-heading bg-orange-1">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#rnotifications">
<i class="icon-bell-2"></i> Notifications
<span class="label bg-darkblue-1 pull-right">4</span>
</a>
</h4>
</div>
<div id="rnotifications" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="list-unstyled" id="notification-list">
<li><a href="javascript:;"><span class="icon-wrapper"><i class="icon-video"></i></span> 1 Video Uploaded <span class="muted">12 minutes ago</span></a></li>
<li><a href="javascript:;"><span class="icon-wrapper"><i class="icon-users-1"></i></span> 3 Users signed up <span class="muted">16 minutes ago</span></a></li>
<li><a href="javascript:;"><span class="icon-wrapper"><i class="icon-picture-1"></i></span> 1 Video Uploaded <span class="muted">12 minutes ago</span></a></li>
<li><a href="javascript:;"><span class="icon-wrapper"><i class="icon-hourglass-1"></i></span> Deadline for 1 project <span class="muted">12 minutes ago</span></a></li>
</ul>
<a class="btn btn-block btn-sm btn-warning">See all notifications</a>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading bg-green-3">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#remails">
<i class="icon-mail"></i> E-mails
<span class="label bg-darkblue-1 pull-right">3</span>
</a>
</h4>
</div>
<div id="remails" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="list-unstyled" id="inbox-list">
<li><a href="javascript:;"><span class="sender"><i class="icon-star text-yellow-2"></i> Kim Wilde</span> <span class="datetime">6 mins ago</span>
<span class="title">You keep me hangin on</span>
<span class="content">Where are you? I am waiting for 3 hours in the restaurant. I ate 3 hamburgers.</span>
</a></li>
<li><a href="javascript:;"><span class="sender">Tyler Durden</span> <span class="datetime">13 hours ago</span>
<span class="title">Buy some soap from market before</span>
<span class="content">We are crowded here. We need some more soap at home. Buy some before you come home.</span>
</a></li>
<li><a href="javascript:;"><span class="sender">John Bonomo</span> <span class="datetime">Yesterday</span>
<span class="title">Late delivery</span>
<span class="content">Hello, I ordered 15 box of viagra for a friend of mine but he still hasn't receive them.</span>
</a></li>
</ul>
<a class="btn btn-block btn-sm btn-primary">Go to inbox</a>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading bg-blue-1">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#rupdates">
<i class="icon-signal-2"></i> Updates
<span class="label bg-darkblue-1 pull-right">5</span>
</a>
</h4>
</div>
<div id="rupdates" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="list-unstyled" id="updates-list">
<li><a href="javascript:;"><span class="icon-wrapper bg-green-1"><i class="icon-comment-1"></i></span> <b>David Guetta</b> came online <abbr title="15 seconds ago">just now</abbr>.</a></li>
<li><a href="javascript:;"><span class="icon-wrapper bg-red-1"><i class="icon-user-3"></i></span> <b>Katy Perry</b> updated her profile <abbr title="4 mins ago">4 mins ago</abbr>.</a></li>
<li><a href="javascript:;"><span class="icon-wrapper bg-blue-1"><i class="icon-twitter-2"></i></span> <b>4 tweets posted</b> with cronjob <abbr title="22 mins ago">22 mins ago</abbr>.</a></li>
<li><a href="javascript:;"><span class="icon-wrapper bg-orange-3"><i class="icon-water"></i></span> <b>Adele</b> set fire to the rain <abbr title="43 mins ago">43 mins ago</abbr>.</a></li>
<li><a href="javascript:;"><span class="icon-wrapper bg-pink-2"><i class="icon-heart-broken"></i></span> <b>Taylor Swift</b> learned that you are trouble <abbr title="3 hours ago">3 days ago</abbr>.</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="connect">
<div class="tab-inner slimscroller">
<div class="search-right">
<input type="text" class="form-control" placeholder="Search">
</div>
<div class="panel-group" id="collapse">
<div class="panel panel-default" id="chat-panel">
<div class="panel-heading bg-darkblue-2">
<h4 class="panel-title">
<a data-toggle="collapse" href="#chat-coll">
<i class="icon-briefcase-1"></i> Colleagues
<span class="label bg-darkblue-1 pull-right">14</span>
</a>
</h4>
</div>
<div id="chat-coll" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="list-unstyled" id="chat-list">
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/1.jpg"></span> <span class="chat-user-name">Dorothy Simons</span><span class="chat-user-msg">I wish I was a bird in the sky</span></a></li>
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/2.jpg"></span> <span class="chat-user-name">John Malkovich</span><span class="chat-user-msg">You were the traitor</span></a></li>
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/3.jpg"></span> <span class="chat-user-name">Jessica Simons</span><span class="chat-user-msg">Where is my mind</span></a></li>
<li><a href="javascript:;" class="away"><span class="chat-user-avatar"><img src="admin/images/users/chat/4.jpg"></span> <span class="chat-user-name">Jack Stallman</span><span class="chat-user-msg">Away since 13:32</span></a></li>
<li><a href="javascript:;" class="offline"><span class="chat-user-avatar"><img src="admin/images/users/chat/5.jpg"></span> <span class="chat-user-name">Neil Armstrong</span><span class="chat-user-msg" title="I am flying to the moon and back">I am flying to the moon and back</span></a></li>
<li><a href="javascript:;" class="offline"><span class="chat-user-avatar"><img src="admin/images/users/chat/6.jpg"></span> <span class="chat-user-name">Hollywood Studios</span><span class="chat-user-msg">Yes he definitely is!</span></a></li>
</ul>
</div>
</div>
</div>
<div class="panel panel-default" id="chat-panel">
<div class="panel-heading bg-darkblue-2">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo">
<i class="icon-heart-3"></i> Friends
<span class="label bg-darkblue-1 pull-right">3</span>
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="list-unstyled" id="chat-list">
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/1.jpg"></span> <span class="chat-user-name">Dorothy Simons</span><span class="chat-user-msg">I wish I was a bird in the sky</span></a></li>
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/2.jpg"></span> <span class="chat-user-name">John Malkovich</span><span class="chat-user-msg">You were the traitor</span></a></li>
<li><a href="javascript:;" class="online"><span class="chat-user-avatar"><img src="admin/images/users/chat/3.jpg"></span> <span class="chat-user-name">Jessica Simons</span><span class="chat-user-msg" title="Eminem - The Monster ft. Rihanna"><i class="icon-play"></i> Eminem - The Monster ft. Rihanna</span></a></li>
<li><a href="javascript:;" class="away"><span class="chat-user-avatar"><img src="admin/images/users/chat/4.jpg"></span> <span class="chat-user-name">Jack Stallman</span><span class="chat-user-msg">Away since 13:32</span></a></li>
<li><a href="javascript:;" class="offline"><span class="chat-user-avatar"><img src="admin/images/users/chat/5.jpg"></span> <span class="chat-user-name">Neil Armstrong</span><span class="chat-user-msg" title="I am flying to the moon and back">I am flying to the moon and back</span></a></li>
<li><a href="javascript:;" class="offline"><span class="chat-user-avatar"><img src="admin/images/users/chat/6.jpg"></span> <span class="chat-user-name">Hollywood Studios</span><span class="chat-user-msg">Yes he definitely is!</span></a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="settings">
<div class="tab-inner slimscroller">
<div class="col-sm-12">
<h3>Preferences</h3>
<div class="row">
<div class="col-xs-8">
Live data updates
</div>
<div class="col-xs-4">
<input type="checkbox" class="ios-switch ios-switch-success ios-switch-sm" checked />
</div>
</div>
<div class="row">
<div class="col-xs-8">
Live feeds
</div>
<div class="col-xs-4">
<input type="checkbox" class="ios-switch ios-switch-success ios-switch-sm" checked />
</div>
</div>
<div class="row">
<div class="col-xs-8">
Sync data to cloud
</div>
<div class="col-xs-4">
<input type="checkbox" class="ios-switch ios-switch-success ios-switch-sm" checked />
</div>
</div>
<div class="row">
<div class="col-xs-8">
Keep activity record
</div>
<div class="col-xs-4">
<input type="checkbox" class="ios-switch ios-switch-danger ios-switch-sm" checked />
</div>
</div>
<h4>Other Settings</h4>
<div class="row">
<div class="col-xs-12">
<label class="checkboxw"><input type="checkbox" checked> Autosave settings</label>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<label class="checkboxw"><input type="checkbox"> Always online</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Right Sidebar End -->
<%= yield %>
</div>
<div id="contextMenu" class="dropdown clearfix">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="javascript:;" data-priority="high"><i class="fa fa-circle-o text-red-1"></i> High Priority</a></li>
<li><a tabindex="-1" href="javascript:;" data-priority="medium"><i class="fa fa-circle-o text-orange-3"></i> Medium Priority</a></li>
<li><a tabindex="-1" href="javascript:;" data-priority="low"><i class="fa fa-circle-o text-yellow-1"></i> Low Priority</a></li>
<li><a tabindex="-1" href="javascript:;" data-priority="none"><i class="fa fa-circle-o text-lightblue-1"></i> None</a></li>
</ul>
</div>
<!-- End of page -->
<!-- the overlay modal element -->
<div class="md-overlay"></div>
<!-- End of eoverlay modal -->
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</body>
<div class="admin-modal-container"></div>
</body>
</html>

@ -0,0 +1 @@
admins-users: admins-users

@ -0,0 +1,22 @@
defaults: &defaults
access_key_id: 'test'
access_key_secret: 'test'
base_url: 'http://vod.cn-shanghai.aliyuncs.com'
cate_id: '-1'
callback_url: 'http://47.96.87.25:48080/api/callbacks/aliyun_vod.json'
signature_key: 'test12345678'
development:
<<: *defaults
access_key_id: 'LTAI4kRL1DxQPdM2'
access_key_secret: 'yiz68rxE6imziBTITggWcOeSqjUeUu'
cate_id: '1000068305'
base_url: 'http://vod.cn-shanghai.aliyuncs.com'
callback_url: 'http://47.96.87.25:48080/api/callbacks/aliyun_vod.json'
signature_key: 'sdgdfDGH14DHD5g465123'
test:
<<: *defaults
production:
<<: *defaults

@ -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.css )
Rails.application.config.assets.precompile += %w( admin.js admin.scss )

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

Loading…
Cancel
Save