From e15e24b36978beb01d0834b88a4a4d64cb42137e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=A0=91=E6=98=8E?= <775174143@qq.com> Date: Mon, 2 Sep 2019 13:48:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E9=98=BF=E9=87=8C=E4=BA=91?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/javascripts/admin.js | 26 +- .../javascripts/admins/common-refuse-modal.js | 3 +- .../javascripts/admins/dashboards/index.js | 66 + .../javascripts/admins/departments/index.js | 173 + .../admins/library_applies/index.js | 20 + .../admins/mirror_repositories/edit.js | 19 + .../admins/mirror_repositories/index.js | 4 + .../javascripts/admins/mirror_scripts/form.js | 33 + .../modals/admin-choose-mirror-modal.js | 32 + .../modals/admin-edit-department-modal.js | 34 + .../modals/admin-merge-department-modal.js | 110 + .../modals/admin-replace-mirror-modal.js | 89 + .../admins/modals/admin-upload-file-modal.js | 62 + .../project_package_applies.js | 20 + .../admins/shixun_settings/index.js | 44 + .../javascripts/admins/shixuns/index.js | 14 + .../javascripts/admins/video_applies/index.js | 20 + app/assets/javascripts/college.js | 21 + app/assets/javascripts/colleges/statistics.js | 156 + app/assets/javascripts/homework_banks.js | 2 + app/assets/stylesheets/admin.scss | 33 +- app/assets/stylesheets/admins/common.scss | 4 + app/assets/stylesheets/admins/dashboards.scss | 7 + .../stylesheets/admins/departments.scss | 24 + .../stylesheets/admins/library_applies.scss | 9 + .../admins/project_package_apply.scss | 9 + app/assets/stylesheets/admins/shixun.scss | 7 + .../stylesheets/admins/shixun_settings.scss | 22 + .../stylesheets/admins/video_apply.scss | 9 + app/assets/stylesheets/college.scss | 13 + .../stylesheets/colleges/statistic.scss | 135 + app/assets/stylesheets/common.scss | 36 + app/assets/stylesheets/homework_banks.scss | 3 + .../stylesheets/select2-bootstrap4.min.scss | 2 +- .../choose_mirror_repositories_controller.rb | 11 + .../admins/dashboards_controller.rb | 47 + .../admins/department_members_controller.rb | 20 + .../admins/departments_controller.rb | 95 + app/controllers/admins/files_controller.rb | 54 + .../admins/library_applies_controller.rb | 25 + .../admins/mirror_repositories_controller.rb | 97 + .../admins/mirror_scripts_controller.rb | 59 + .../admins/myshixuns_controller.rb | 14 + .../project_package_applies_controller.rb | 37 + .../admins/shixun_settings_controller.rb | 97 + app/controllers/admins/shixuns_controller.rb | 64 + .../admins/video_applies_controller.rb | 41 + app/controllers/application_controller.rb | 6 +- app/controllers/challenges_controller.rb | 23 +- app/controllers/colleges_controller.rb | 171 + .../concerns/admins/render_helper.rb | 48 +- app/controllers/concerns/git_common.rb | 14 +- app/controllers/courses_controller.rb | 224 +- .../exercise_bank_questions_controller.rb | 418 + app/controllers/exercise_banks_controller.rb | 129 + .../exercise_questions_controller.rb | 18 +- app/controllers/files_controller.rb | 23 +- app/controllers/games_controller.rb | 53 - .../graduation_works_controller.rb | 2 +- app/controllers/gtopic_banks_controller.rb | 40 + app/controllers/homework_banks_controller.rb | 69 + .../poll_bank_questions_controller.rb | 158 + app/controllers/polls_controller.rb | 6 +- app/controllers/question_banks_controller.rb | 59 +- app/controllers/shixuns_controller.rb | 23 +- app/controllers/student_works_controller.rb | 20 +- app/controllers/subjects_controller.rb | 4 +- app/controllers/task_banks_controller.rb | 49 + .../users/question_banks_controller.rb | 29 +- .../admins/mirror_repositories_helper.rb | 23 + app/helpers/exercise_bank_questions_helper.rb | 2 + app/helpers/homework_banks_helper.rb | 2 + app/helpers/homework_commons_helper.rb | 2 +- app/helpers/shixuns_helper.rb | 13 + app/jobs/delete_department_notify_job.rb | 21 + app/models/challenge.rb | 8 + app/models/customer.rb | 10 + app/models/department.rb | 12 + app/models/exercise_bank.rb | 3 + app/models/exercise_bank_choice.rb | 3 + app/models/exercise_bank_question.rb | 23 +- app/models/exercise_bank_standard_answer.rb | 1 + app/models/homework_common.rb | 2 +- app/models/library_apply.rb | 4 + app/models/mirror_operation_record.rb | 7 + app/models/mirror_repository.rb | 4 + app/models/myshixun.rb | 3 + app/models/partner.rb | 2 + app/models/partner_customer.rb | 4 + app/models/school.rb | 3 + app/models/shixun.rb | 27 + app/models/student_work.rb | 1 + app/models/user.rb | 2 +- app/queries/admins/department_query.rb | 32 + app/queries/admins/library_apply_query.rb | 29 + app/queries/admins/myshixun_query.rb | 26 + app/queries/admins/shixun_query.rb | 48 + app/queries/admins/shixun_settings_query.rb | 54 + app/queries/admins/user_query.rb | 8 +- .../admins/add_department_member_service.rb | 20 + .../admins/check_shixun_mirrors_service.rb | 89 + app/services/admins/choose_mirror_service.rb | 21 + .../admins/save_mirror_repository_service.rb | 37 + app/services/homeworks_service.rb | 7 +- app/services/users/question_bank_service.rb | 12 +- app/templates/shared/main.css | 2 +- .../shixun_work/shixun_work.html.erb | 10 +- .../choose_mirror_repositories/create.js.erb | 5 + .../choose_mirror_repositories/new.js.erb | 2 + .../daily_school_statistics/index.html.erb | 2 +- app/views/admins/dashboards/index.html.erb | 244 +- .../admins/department_members/create.js.erb | 4 + .../admins/department_members/destroy.js.erb | 2 + app/views/admins/departments/edit.js.erb | 2 + app/views/admins/departments/index.html.erb | 33 + app/views/admins/departments/index.js.erb | 1 + .../_add_department_member_modal.html.erb | 30 + .../shared/_create_department_modal.html.erb | 35 + .../shared/_department_item.html.erb | 34 + .../shared/_edit_department_modal.html.erb | 25 + .../admins/departments/shared/_list.html.erb | 28 + .../departments/shared/_member_users.html.erb | 12 + .../shared/_merge_department_modal.html.erb | 31 + app/views/admins/departments/update.js.erb | 4 + .../identity_authentications/index.html.erb | 2 +- .../admins/library_applies/index.html.erb | 32 + app/views/admins/library_applies/index.js.erb | 1 + .../library_applies/shared/_list.html.erb | 56 + .../admins/mirror_repositories/edit.html.erb | 8 + .../admins/mirror_repositories/index.html.erb | 23 + .../admins/mirror_repositories/index.js.erb | 1 + .../admins/mirror_repositories/merge.js.erb | 5 + .../admins/mirror_repositories/new.html.erb | 8 + .../shared/_choose_mirror_modal.html.erb | 42 + .../mirror_repositories/shared/_form.html.erb | 42 + .../mirror_repositories/shared/_list.html.erb | 54 + .../shared/_replace_mirror_modal.html.erb | 33 + app/views/admins/mirror_scripts/edit.html.erb | 8 + .../admins/mirror_scripts/index.html.erb | 14 + app/views/admins/mirror_scripts/new.html.erb | 8 + .../mirror_scripts/shared/_form.html.erb | 12 + .../mirror_scripts/shared/_list.html.erb | 32 + app/views/admins/myshixuns/index.html.erb | 14 + app/views/admins/myshixuns/index.js.erb | 1 + .../admins/myshixuns/shared/_list.html.erb | 49 + .../index.html.erb | 2 +- .../project_package_applies/index.html.erb | 32 + .../project_package_applies/index.js.erb | 1 + .../shared/_list.html.erb | 56 + app/views/admins/shared/403.html.erb | 6 + app/views/admins/shared/500.html.erb | 2 +- .../_admin_common_refuse_modal.html.erb | 6 +- app/views/admins/shared/_paginate.html.erb | 2 +- app/views/admins/shared/_sidebar.html.erb | 23 +- .../shared/modal/_upload_file_modal.html.erb | 32 + .../shixun_authorizations/index.html.erb | 2 +- .../admins/shixun_settings/index.html.erb | 76 + app/views/admins/shixun_settings/index.js.erb | 1 + .../shixun_settings/shared/_list.html.erb | 35 + .../shixun_settings/shared/_td.html.erb | 51 + .../admins/shixun_settings/update.js.erb | 1 + app/views/admins/shixuns/index.html.erb | 32 + app/views/admins/shixuns/index.js.erb | 1 + .../admins/shixuns/shared/_list.html.erb | 55 + .../subject_authorizations/index.html.erb | 2 +- app/views/admins/users/edit.html.erb | 2 +- app/views/admins/users/index.html.erb | 2 +- app/views/admins/users/index.json.jbuilder | 6 + .../users/shared/_reward_grade_modal.html.erb | 2 +- app/views/admins/video_applies/index.html.erb | 32 + app/views/admins/video_applies/index.js.erb | 1 + .../video_applies/shared/_list.html.erb | 57 + .../challenges/crud_answer.json.jbuilder | 2 +- .../colleges/_course_statistics.html.erb | 45 + app/views/colleges/_student_rank.html.erb | 23 + app/views/colleges/_teacher_rank.html.erb | 21 + app/views/colleges/course_statistics.js.erb | 1 + app/views/colleges/shared/_navbar.html.erb | 21 + app/views/colleges/statistics.html.erb | 164 + app/views/colleges/student_shixun.js.erb | 1 + app/views/colleges/teachers.js.erb | 1 + .../courses/export_course_info.xlsx.axlsx | 29 + .../export_member_act_score.xlsx.axlsx | 25 + .../export_member_scores_excel.xlsx.axlsx | 38 - .../_exercise_bank_questions.json.jbuilder | 72 + .../choose_shixun.json.jbuilder | 17 + .../commit_shixun.json.jbuilder | 10 + app/views/exercise_banks/show.json.jbuilder | 51 + .../_exercise_questions.json.jbuilder | 6 +- app/views/exercises/index.json.jbuilder | 1 + .../exercises/start_answer.json.jbuilder | 7 +- app/views/games/picture_display.json.jbuilder | 2 +- app/views/gtopic_banks/edit.json.jbuilder | 15 + app/views/gtopic_banks/show.json.jbuilder | 6 + app/views/homework_banks/show.json.jbuilder | 9 + .../homework_commons/index.json.jbuilder | 1 + app/views/layouts/admin.html.erb | 1 + app/views/layouts/college.html.erb | 24 + app/views/polls/commit_result.json.jbuilder | 2 +- app/views/polls/index.json.jbuilder | 1 + .../question_banks/my_courses.json.jbuilder | 4 + app/views/shixuns/add_file.json.jbuilder | 1 + .../adjust_review_score.json.jbuilder | 5 +- .../shixun_work_report.json.jbuilder | 1 + app/views/student_works/show.json.jbuilder | 3 +- app/views/task_banks/show.json.jbuilder | 14 + .../users/question_banks/index.json.jbuilder | 15 +- config/admins/sidebar.yml | 2 +- config/initializers/assets.rb | 4 +- config/locales/library_applies/zh-CN.yml | 7 + config/locales/zh-CN.yml | 15 +- config/routes.rb | 106 +- ...222_add_update_user_id_to_student_works.rb | 6 + ...dd_is_ordered_to_exercise_bank_question.rb | 5 + ...829025855_migrate_gtopic_bank_is_public.rb | 5 + ...829084147_migrate_course_group_position.rb | 11 + ...4803_add_announcement_to_course_modules.rb | 10 + .../20190830082934_add_tag_to_libraries.rb | 5 + ...190831014325_migrate_poll_bank_question.rb | 6 + db/seeds.rb | 44 +- lib/tasks/public_course_zhp.rake | 171 + ...fest-1c370772f16743f825981ab0e5c94237.json | 1 - ...fest-fd1f19755cf79ae07a20ee9d2676d85e.json | 1 + ...da5443521854cd6fe7eecf750cf443f1e699c9.css | 19193 +++ ...443521854cd6fe7eecf750cf443f1e699c9.css.gz | Bin 0 -> 52434 bytes ...8bd28c4aff07b70bed9b41faf6899a89af4b57d.js | 28296 ---- ...28c4aff07b70bed9b41faf6899a89af4b57d.js.gz | Bin 219374 -> 0 bytes ...ac4a62d8b76f65baf7b06fdedc47323635bcdaf.js | 129292 ++++++++++++++ ...a62d8b76f65baf7b06fdedc47323635bcdaf.js.gz | Bin 0 -> 896729 bytes ...4cae77382331beba55b1570b3d3c8aa42442d5.css | 18405 -- ...e77382331beba55b1570b3d3c8aa42442d5.css.gz | Bin 49605 -> 0 bytes ...5537a46deb578488e47bbb006d551b54f72895.css | 50125 ++++++ ...7a46deb578488e47bbb006d551b54f72895.css.gz | Bin 0 -> 132235 bytes ...304541807c3ea985eb566bdabdbafee7f6c6735.js | 131164 +++++++++++++++ ...541807c3ea985eb566bdabdbafee7f6c6735.js.gz | Bin 0 -> 935077 bytes ...c85a9ac11e536edc73fef6e7489723bf535e533.js | 29988 ---- ...a9ac11e536edc73fef6e7489723bf535e533.js.gz | Bin 256348 -> 0 bytes ...c14745cec09853ea7d91c6fae856b96e788f46.css | 33017 ---- ...745cec09853ea7d91c6fae856b96e788f46.css.gz | Bin 86666 -> 0 bytes ...2187815c096c25e0ab74aba341ae916166cd287.js | 109342 ++++++++++++ ...7815c096c25e0ab74aba341ae916166cd287.js.gz | Bin 0 -> 711176 bytes ...4c4a620f522278a4668cc218465ad8f539966f.css | 15969 ++ ...a620f522278a4668cc218465ad8f539966f.css.gz | Bin 0 -> 42961 bytes public/editormd/lib/marked.min.backup.js | 18 + public/katex/katex.min.css | 3 +- public/katex/katex.min.js | 6 +- public/react/config/webpack.config.dev.js | 3 +- public/react/config/webpack.config.prod.js | 5 +- public/react/public/css/css_min_all.css | 2 +- public/react/public/css/edu-all.css | 2 +- public/react/public/css/edu-common.css | 2 +- .../react/public/js/editormd/editormd.min.js | 7 + public/react/public/js/js_min_all.js | 4 +- public/react/public/katex/katex.min.css | 3 +- public/react/public/katex/katex.min.js | 6 +- public/react/src/App.css | 3 + public/react/src/App.js | 1 - public/react/src/common/TextUtil.js | 4 +- public/react/src/common/UrlTool.js | 16 +- .../src/common/components/DragValidator.js | 149 +- .../components/attachment/AttachmentList.js | 15 +- public/react/src/common/course/ActionBtn.js | 2 +- public/react/src/common/educoder.js | 2 +- .../context/EvaluateSuccessEffectDisplay.js | 2 +- public/react/src/modules/courses/Index.js | 49 +- .../src/modules/courses/Resource/index.js | 42 +- .../src/modules/courses/boards/BoardsNew.js | 37 +- .../react/src/modules/courses/boards/index.js | 7 +- .../courses/busyWork/CommonWorkAppraise.js | 12 +- .../courses/busyWork/CommonWorkDetailIndex.js | 6 +- .../courses/busyWork/CommonWorkItem.js | 1 + .../courses/busyWork/CommonWorkList.js | 28 +- .../busyWork/CommonWorkListTemplate.js | 268 +- .../courses/busyWork/CommonWorkPost.js | 58 +- .../courses/busyWork/CommonWorkQuestion.js | 266 +- .../src/modules/courses/busyWork/NewWork.js | 31 +- .../courses/busyWork/common/LeaderIcon.js | 11 +- .../busyWork/common/WorkDetailPageHeader.js | 23 +- .../modules/courses/busyWork/commonWork.js | 5 +- .../busyWork/reply/CommonWorkAppraiseReply.js | 9 +- .../courses/common/comments/CCommentItem.js | 15 +- .../Completetaskdetails.js | 84 + .../completetaskdetails/Completetaskpage.js | 138 + .../completetaskdetails.css | 58 + .../comtopicdetails/CompletetopicdePage.js | 141 + .../comtopicdetails/Completetopicdetails.js | 115 + .../comtopicdetails/completetopicde.css | 58 + .../courses/coursesDetail/CoursesLeftNav.js | 8 +- .../courses/coursesPublic/AccessoryModal.js | 7 +- .../courses/coursesPublic/AccessoryModal2.js | 6 +- .../courses/coursesPublic/PathModal.js | 11 +- .../courses/coursesPublic/SelectResource.js | 4 +- .../courses/coursesPublic/SelectSetting.js | 65 +- .../courses/coursesPublic/ShixunModal.js | 11 +- .../courses/coursesPublic/Startshixuntask.js | 3 +- .../coursesPublic/modal/ShixunModal2.js | 4 +- .../courses/coursesPublic/sendResource.js | 52 +- .../react/src/modules/courses/css/Courses.css | 19 +- .../react/src/modules/courses/css/members.css | 7 + .../modules/courses/elearning/Elearning.js | 13 +- .../courses/elearning/YslDetailCards.js | 2 +- .../courses/exercise/Ecerciseallbackagain.js | 5 +- .../src/modules/courses/exercise/Exercise.js | 5 +- .../courses/exercise/ExerciseListItem.js | 1 + .../exercise/ExerciseReviewAndAnswer.js | 13 +- .../courses/exercise/Exercisesetting.js | 13 +- .../exercise/Testpapersettinghomepage.js | 16 +- .../courses/exercise/new/JudgeEditor.js | 4 +- .../courses/exercise/question/fillEmpty.js | 6 +- .../courses/exercise/question/simpleAnswer.js | 4 +- .../courses/gradinforms/Bullsubdirectory.js | 4 +- .../modules/courses/gradinforms/Eduinforms.js | 12 +- .../courses/gradinforms/myysleduinforms.css | 6 +- .../tasks/GraduationTasksSubmitedit.js | 90 +- .../tasks/GraduationTasksSubmitnew.js | 25 +- .../GraduationTasksappraiseMainEditor.js | 39 +- .../graduation/tasks/GraduationTasksedit.js | 89 +- .../graduation/tasks/GraduationTasksnew.js | 101 +- .../tasks/GraduationTaskssetting.js | 3 +- .../tasks/GraduationTaskssettinglist.js | 73 +- .../tasks/GraduationTaskssettingquestions.js | 2 +- .../modules/courses/graduation/tasks/index.js | 5 +- .../graduation/topics/GraduateTopicDetail.js | 2 +- .../graduation/topics/GraduateTopicNew.js | 32 +- .../topics/GraduateTopicPostWorksNew.js | 34 +- .../courses/graduation/topics/index.js | 5 +- .../courses/groupjobbank/GroupPackage.js | 87 + .../courses/groupjobbank/GroupPackage2.js | 73 + .../groupjobbank/Groupjobbandetails.js | 84 + .../courses/groupjobbank/GroupjobbankPage.js | 175 + .../groupjobbank/Groupjobquesanswer.js | 82 + .../courses/groupjobbank/questionbanks.css | 41 + .../members/modal/CreateGroupByImportModal.js | 28 +- .../modules/courses/members/studentsList.js | 157 +- .../modules/courses/members/teacherList.js | 40 +- .../src/modules/courses/new/CoursesNew.js | 278 +- .../src/modules/courses/new/Goldsubject.js | 389 +- public/react/src/modules/courses/poll/Poll.js | 31 +- .../courses/poll/PollDetailTabThird.js | 4 +- .../src/modules/courses/poll/PollListItem.js | 3 +- .../react/src/modules/courses/poll/PollNew.js | 254 +- .../courses/questionbank/Generaljobanswer.js | 79 + .../questionbank/Generaljobbankdetails.js | 173 + .../courses/questionbank/Generaljobdetails.js | 81 + .../courses/questionbank/questionbank.css | 26 + .../shixunHomework/ShixunHomeworkPage.js | 2 +- .../shixunHomework/ShixunWorkReport.js | 62 +- .../shixunHomework/ShixunhomeWorkItem.js | 5 +- .../Shixunworkdetails/ShixunWorkModal.js | 5 +- .../courses/shixunHomework/shixunHomework.js | 31 +- .../shixunreport/ConclusionEvaluation.js | 55 +- .../OfficialAcademicTranscript.js | 150 +- .../courses/studentWork/StudentHomework.js | 2 +- .../src/modules/forums/MemoDetailMDEditor.js | 10 +- public/react/src/modules/forums/MemoNew.js | 1676 +- .../modules/forums/shixun/ShiXunPostItem.js | 2 +- .../src/modules/message/js/MessagChat.js | 4 +- .../src/modules/moop_cases/CaseDetail.js | 4 +- .../react/src/modules/moop_cases/CaseNew.js | 905 +- .../react/src/modules/moop_cases/CaseTags.js | 12 +- .../src/modules/moop_cases/css/moopCases.css | 7 + public/react/src/modules/page/Index.js | 12 +- .../src/modules/page/MainContentContainer.js | 4 +- .../modules/page/component/TPICodeSetting.js | 54 +- .../modules/paths/PathDetail/DetailCards.js | 2 +- .../paths/PathDetail/DetailCardsEditAndAdd.js | 5 +- .../PathDetail/DetailCardsEditAndEdit.js | 5 +- .../src/modules/paths/PathDetail/DetailTop.js | 34 +- .../paths/PathDetail/addCollaborators.js | 7 +- public/react/src/modules/test/ShareTest.js | 35 + public/react/src/modules/tpm/NewHeader.js | 21 +- public/react/src/modules/tpm/TPMBanner.js | 5 +- public/react/src/modules/tpm/TPMIndex.js | 39 +- public/react/src/modules/tpm/TPMIndexHOC.js | 80 +- .../src/modules/tpm/TPMRepositoryComponent.js | 7 + .../TPMUpdatepropaede/TPMUpdatepropaede.js | 134 +- .../modules/tpm/TPMsettings/TPMsettings.js | 115 +- .../modules/tpm/challengesnew/TPManswer2.js | 6 +- .../modules/tpm/challengesnew/TPMquestion.js | 37 +- .../tpm/challengesnew/TpmQuestionEdit.js | 17 +- .../tpm/challengesnew/TpmQuestionNew.js | 4 +- .../src/modules/tpm/component/TPMright.css | 2 +- .../src/modules/tpm/newshixuns/Newshixuns.js | 49 +- .../tpm/shixunchild/Challenges/Challenges.js | 2 +- .../Collaborators/Collaborators.js | 4 +- .../tpm/shixunchild/Repository/Repository.js | 25 +- .../Repository/RepositoryAddFile.js | 198 + .../modules/user/account/AccountBasicEdit.js | 2 +- .../src/modules/user/usersInfo/InfosBanner.js | 254 +- .../user/usersInfo/banks/BanksIndex.js | 214 + public/react/src/scripts/build.js | 189 - public/react/src/scripts/concat.js | 169 - public/react/src/scripts/start.js | 114 - public/react/src/scripts/test.js | 27 - public/stylesheets/css/edu-common.css | 2 +- public/stylesheets/educoder/edu-all.css | 11 +- public/stylesheets/educoder/edu-main.css | 23 +- .../homework_banks_controller_spec.rb | 5 + spec/helpers/homework_banks_helper_spec.rb | 15 + vendor/assets/codemirror/lib/codemirror.css | 349 + vendor/assets/codemirror/lib/codemirror.js | 9765 ++ vendor/assets/codemirror/mode/shell/shell.js | 152 + vendor/assets/javascripts/echarts.js | 90302 ++++++++++ 403 files changed, 567718 insertions(+), 113686 deletions(-) create mode 100644 app/assets/javascripts/admins/dashboards/index.js create mode 100644 app/assets/javascripts/admins/departments/index.js create mode 100644 app/assets/javascripts/admins/library_applies/index.js create mode 100644 app/assets/javascripts/admins/mirror_repositories/edit.js create mode 100644 app/assets/javascripts/admins/mirror_repositories/index.js create mode 100644 app/assets/javascripts/admins/mirror_scripts/form.js create mode 100644 app/assets/javascripts/admins/modals/admin-choose-mirror-modal.js create mode 100644 app/assets/javascripts/admins/modals/admin-edit-department-modal.js create mode 100644 app/assets/javascripts/admins/modals/admin-merge-department-modal.js create mode 100644 app/assets/javascripts/admins/modals/admin-replace-mirror-modal.js create mode 100644 app/assets/javascripts/admins/modals/admin-upload-file-modal.js create mode 100644 app/assets/javascripts/admins/project_package_applies/project_package_applies.js create mode 100644 app/assets/javascripts/admins/shixun_settings/index.js create mode 100644 app/assets/javascripts/admins/shixuns/index.js create mode 100644 app/assets/javascripts/admins/video_applies/index.js create mode 100644 app/assets/javascripts/college.js create mode 100644 app/assets/javascripts/colleges/statistics.js create mode 100644 app/assets/javascripts/homework_banks.js create mode 100644 app/assets/stylesheets/admins/dashboards.scss create mode 100644 app/assets/stylesheets/admins/departments.scss create mode 100644 app/assets/stylesheets/admins/library_applies.scss create mode 100644 app/assets/stylesheets/admins/project_package_apply.scss create mode 100644 app/assets/stylesheets/admins/shixun.scss create mode 100644 app/assets/stylesheets/admins/shixun_settings.scss create mode 100644 app/assets/stylesheets/admins/video_apply.scss create mode 100644 app/assets/stylesheets/college.scss create mode 100644 app/assets/stylesheets/colleges/statistic.scss create mode 100644 app/assets/stylesheets/common.scss create mode 100644 app/assets/stylesheets/homework_banks.scss create mode 100644 app/controllers/admins/choose_mirror_repositories_controller.rb create mode 100644 app/controllers/admins/department_members_controller.rb create mode 100644 app/controllers/admins/departments_controller.rb create mode 100644 app/controllers/admins/files_controller.rb create mode 100644 app/controllers/admins/library_applies_controller.rb create mode 100644 app/controllers/admins/mirror_repositories_controller.rb create mode 100644 app/controllers/admins/mirror_scripts_controller.rb create mode 100644 app/controllers/admins/myshixuns_controller.rb create mode 100644 app/controllers/admins/project_package_applies_controller.rb create mode 100644 app/controllers/admins/shixun_settings_controller.rb create mode 100644 app/controllers/admins/shixuns_controller.rb create mode 100644 app/controllers/admins/video_applies_controller.rb create mode 100644 app/controllers/colleges_controller.rb create mode 100644 app/controllers/exercise_bank_questions_controller.rb create mode 100644 app/controllers/exercise_banks_controller.rb create mode 100644 app/controllers/gtopic_banks_controller.rb create mode 100644 app/controllers/homework_banks_controller.rb create mode 100644 app/controllers/poll_bank_questions_controller.rb create mode 100644 app/controllers/task_banks_controller.rb create mode 100644 app/helpers/admins/mirror_repositories_helper.rb create mode 100644 app/helpers/exercise_bank_questions_helper.rb create mode 100644 app/helpers/homework_banks_helper.rb create mode 100644 app/jobs/delete_department_notify_job.rb create mode 100644 app/models/customer.rb create mode 100644 app/models/mirror_operation_record.rb create mode 100644 app/models/partner_customer.rb create mode 100644 app/queries/admins/department_query.rb create mode 100644 app/queries/admins/library_apply_query.rb create mode 100644 app/queries/admins/myshixun_query.rb create mode 100644 app/queries/admins/shixun_query.rb create mode 100644 app/queries/admins/shixun_settings_query.rb create mode 100644 app/services/admins/add_department_member_service.rb create mode 100644 app/services/admins/check_shixun_mirrors_service.rb create mode 100644 app/services/admins/choose_mirror_service.rb create mode 100644 app/services/admins/save_mirror_repository_service.rb create mode 100644 app/views/admins/choose_mirror_repositories/create.js.erb create mode 100644 app/views/admins/choose_mirror_repositories/new.js.erb create mode 100644 app/views/admins/department_members/create.js.erb create mode 100644 app/views/admins/department_members/destroy.js.erb create mode 100644 app/views/admins/departments/edit.js.erb create mode 100644 app/views/admins/departments/index.html.erb create mode 100644 app/views/admins/departments/index.js.erb create mode 100644 app/views/admins/departments/shared/_add_department_member_modal.html.erb create mode 100644 app/views/admins/departments/shared/_create_department_modal.html.erb create mode 100644 app/views/admins/departments/shared/_department_item.html.erb create mode 100644 app/views/admins/departments/shared/_edit_department_modal.html.erb create mode 100644 app/views/admins/departments/shared/_list.html.erb create mode 100644 app/views/admins/departments/shared/_member_users.html.erb create mode 100644 app/views/admins/departments/shared/_merge_department_modal.html.erb create mode 100644 app/views/admins/departments/update.js.erb create mode 100644 app/views/admins/library_applies/index.html.erb create mode 100644 app/views/admins/library_applies/index.js.erb create mode 100644 app/views/admins/library_applies/shared/_list.html.erb create mode 100644 app/views/admins/mirror_repositories/edit.html.erb create mode 100644 app/views/admins/mirror_repositories/index.html.erb create mode 100644 app/views/admins/mirror_repositories/index.js.erb create mode 100644 app/views/admins/mirror_repositories/merge.js.erb create mode 100644 app/views/admins/mirror_repositories/new.html.erb create mode 100644 app/views/admins/mirror_repositories/shared/_choose_mirror_modal.html.erb create mode 100644 app/views/admins/mirror_repositories/shared/_form.html.erb create mode 100644 app/views/admins/mirror_repositories/shared/_list.html.erb create mode 100644 app/views/admins/mirror_repositories/shared/_replace_mirror_modal.html.erb create mode 100644 app/views/admins/mirror_scripts/edit.html.erb create mode 100644 app/views/admins/mirror_scripts/index.html.erb create mode 100644 app/views/admins/mirror_scripts/new.html.erb create mode 100644 app/views/admins/mirror_scripts/shared/_form.html.erb create mode 100644 app/views/admins/mirror_scripts/shared/_list.html.erb create mode 100644 app/views/admins/myshixuns/index.html.erb create mode 100644 app/views/admins/myshixuns/index.js.erb create mode 100644 app/views/admins/myshixuns/shared/_list.html.erb create mode 100644 app/views/admins/project_package_applies/index.html.erb create mode 100644 app/views/admins/project_package_applies/index.js.erb create mode 100644 app/views/admins/project_package_applies/shared/_list.html.erb create mode 100644 app/views/admins/shared/403.html.erb create mode 100644 app/views/admins/shared/modal/_upload_file_modal.html.erb create mode 100644 app/views/admins/shixun_settings/index.html.erb create mode 100644 app/views/admins/shixun_settings/index.js.erb create mode 100644 app/views/admins/shixun_settings/shared/_list.html.erb create mode 100644 app/views/admins/shixun_settings/shared/_td.html.erb create mode 100644 app/views/admins/shixun_settings/update.js.erb create mode 100644 app/views/admins/shixuns/index.html.erb create mode 100644 app/views/admins/shixuns/index.js.erb create mode 100644 app/views/admins/shixuns/shared/_list.html.erb create mode 100644 app/views/admins/users/index.json.jbuilder create mode 100644 app/views/admins/video_applies/index.html.erb create mode 100644 app/views/admins/video_applies/index.js.erb create mode 100644 app/views/admins/video_applies/shared/_list.html.erb create mode 100644 app/views/colleges/_course_statistics.html.erb create mode 100644 app/views/colleges/_student_rank.html.erb create mode 100644 app/views/colleges/_teacher_rank.html.erb create mode 100644 app/views/colleges/course_statistics.js.erb create mode 100644 app/views/colleges/shared/_navbar.html.erb create mode 100644 app/views/colleges/statistics.html.erb create mode 100644 app/views/colleges/student_shixun.js.erb create mode 100644 app/views/colleges/teachers.js.erb create mode 100644 app/views/courses/export_course_info.xlsx.axlsx create mode 100644 app/views/courses/export_member_act_score.xlsx.axlsx create mode 100644 app/views/exercise_bank_questions/_exercise_bank_questions.json.jbuilder create mode 100644 app/views/exercise_banks/choose_shixun.json.jbuilder create mode 100644 app/views/exercise_banks/commit_shixun.json.jbuilder create mode 100644 app/views/exercise_banks/show.json.jbuilder create mode 100644 app/views/gtopic_banks/edit.json.jbuilder create mode 100644 app/views/gtopic_banks/show.json.jbuilder create mode 100644 app/views/homework_banks/show.json.jbuilder create mode 100644 app/views/layouts/college.html.erb create mode 100644 app/views/question_banks/my_courses.json.jbuilder create mode 100644 app/views/shixuns/add_file.json.jbuilder create mode 100644 app/views/task_banks/show.json.jbuilder create mode 100644 config/locales/library_applies/zh-CN.yml create mode 100644 db/migrate/20190828011222_add_update_user_id_to_student_works.rb create mode 100644 db/migrate/20190828055710_add_is_ordered_to_exercise_bank_question.rb create mode 100644 db/migrate/20190829025855_migrate_gtopic_bank_is_public.rb create mode 100644 db/migrate/20190829084147_migrate_course_group_position.rb create mode 100644 db/migrate/20190830064803_add_announcement_to_course_modules.rb create mode 100644 db/migrate/20190830082934_add_tag_to_libraries.rb create mode 100644 db/migrate/20190831014325_migrate_poll_bank_question.rb create mode 100644 lib/tasks/public_course_zhp.rake delete mode 100644 public/assets/.sprockets-manifest-1c370772f16743f825981ab0e5c94237.json create mode 100644 public/assets/.sprockets-manifest-fd1f19755cf79ae07a20ee9d2676d85e.json create mode 100644 public/assets/admin-07f89a76946f8ce796dafa51fbda5443521854cd6fe7eecf750cf443f1e699c9.css create mode 100644 public/assets/admin-07f89a76946f8ce796dafa51fbda5443521854cd6fe7eecf750cf443f1e699c9.css.gz delete mode 100644 public/assets/admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js delete mode 100644 public/assets/admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js.gz create mode 100644 public/assets/admin-81f6ac4c1ad5e53b10117d319ac4a62d8b76f65baf7b06fdedc47323635bcdaf.js create mode 100644 public/assets/admin-81f6ac4c1ad5e53b10117d319ac4a62d8b76f65baf7b06fdedc47323635bcdaf.js.gz delete mode 100644 public/assets/admin-8a2b03cb8a055dc63f45443b304cae77382331beba55b1570b3d3c8aa42442d5.css delete mode 100644 public/assets/admin-8a2b03cb8a055dc63f45443b304cae77382331beba55b1570b3d3c8aa42442d5.css.gz create mode 100644 public/assets/application-1bce1740cf43111049b991ad4d5537a46deb578488e47bbb006d551b54f72895.css create mode 100644 public/assets/application-1bce1740cf43111049b991ad4d5537a46deb578488e47bbb006d551b54f72895.css.gz create mode 100644 public/assets/application-830ab01de7cd41145be3bd392304541807c3ea985eb566bdabdbafee7f6c6735.js create mode 100644 public/assets/application-830ab01de7cd41145be3bd392304541807c3ea985eb566bdabdbafee7f6c6735.js.gz delete mode 100644 public/assets/application-a3a4f3549d68670572bb07700c85a9ac11e536edc73fef6e7489723bf535e533.js delete mode 100644 public/assets/application-a3a4f3549d68670572bb07700c85a9ac11e536edc73fef6e7489723bf535e533.js.gz delete mode 100644 public/assets/application-a7508b88eb6a69a5b301602bddc14745cec09853ea7d91c6fae856b96e788f46.css delete mode 100644 public/assets/application-a7508b88eb6a69a5b301602bddc14745cec09853ea7d91c6fae856b96e788f46.css.gz create mode 100644 public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js create mode 100644 public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz create mode 100644 public/assets/college-279b722d063f61aec89f1ad6f44c4a620f522278a4668cc218465ad8f539966f.css create mode 100644 public/assets/college-279b722d063f61aec89f1ad6f44c4a620f522278a4668cc218465ad8f539966f.css.gz create mode 100644 public/editormd/lib/marked.min.backup.js create mode 100644 public/react/src/modules/courses/completetaskdetails/Completetaskdetails.js create mode 100644 public/react/src/modules/courses/completetaskdetails/Completetaskpage.js create mode 100644 public/react/src/modules/courses/completetaskdetails/completetaskdetails.css create mode 100644 public/react/src/modules/courses/comtopicdetails/CompletetopicdePage.js create mode 100644 public/react/src/modules/courses/comtopicdetails/Completetopicdetails.js create mode 100644 public/react/src/modules/courses/comtopicdetails/completetopicde.css create mode 100644 public/react/src/modules/courses/groupjobbank/GroupPackage.js create mode 100644 public/react/src/modules/courses/groupjobbank/GroupPackage2.js create mode 100644 public/react/src/modules/courses/groupjobbank/Groupjobbandetails.js create mode 100644 public/react/src/modules/courses/groupjobbank/GroupjobbankPage.js create mode 100644 public/react/src/modules/courses/groupjobbank/Groupjobquesanswer.js create mode 100644 public/react/src/modules/courses/groupjobbank/questionbanks.css create mode 100644 public/react/src/modules/courses/questionbank/Generaljobanswer.js create mode 100644 public/react/src/modules/courses/questionbank/Generaljobbankdetails.js create mode 100644 public/react/src/modules/courses/questionbank/Generaljobdetails.js create mode 100644 public/react/src/modules/courses/questionbank/questionbank.css create mode 100644 public/react/src/modules/test/ShareTest.js create mode 100644 public/react/src/modules/tpm/shixunchild/Repository/RepositoryAddFile.js create mode 100644 public/react/src/modules/user/usersInfo/banks/BanksIndex.js delete mode 100644 public/react/src/scripts/build.js delete mode 100644 public/react/src/scripts/concat.js delete mode 100644 public/react/src/scripts/start.js delete mode 100644 public/react/src/scripts/test.js create mode 100644 spec/controllers/homework_banks_controller_spec.rb create mode 100644 spec/helpers/homework_banks_helper_spec.rb create mode 100644 vendor/assets/codemirror/lib/codemirror.css create mode 100644 vendor/assets/codemirror/lib/codemirror.js create mode 100644 vendor/assets/codemirror/mode/shell/shell.js create mode 100644 vendor/assets/javascripts/echarts.js diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 72a2ff118..12b89804b 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -13,15 +13,32 @@ //= require bootstrap-datepicker //= require bootstrap.viewer +//= require echarts +//= require lib/codemirror +//= require mode/shell/shell + //= require_tree ./i18n //= require_tree ./admins + +$.ajaxSetup({ + beforeSend: function(xhr) { + xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); + } +}); + // ******** select2 global config ******** $.fn.select2.defaults.set('theme', 'bootstrap4'); $.fn.select2.defaults.set('language', 'zh-CN'); Turbolinks.setProgressBarDelay(200); +$.notifyDefaults({ + type: 'success', + z_index: 9999, + delay: 2000 +}); + $(document).on('turbolinks:load', function(){ $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="popover"]').popover(); @@ -32,11 +49,18 @@ $(document).on('turbolinks:load', function(){ // flash alert提示框自动关闭 if($('.admin-alert-container .alert').length > 0){ setTimeout(function(){ - $('.admin-alert-container .alert').alert('close'); + $('.admin-alert-container .alert:not(.alert-danger)').alert('close'); }, 2000); + setTimeout(function(){ + $('.admin-alert-container .alert.alert-danger').alert('close'); + }, 5000); } }); +$(document).on("turbolinks:before-cache", function () { + $('[data-toggle="tooltip"]').tooltip('hide'); + $('[data-toggle="popover"]').popover('hide'); +}); // var progressBar = new Turbolinks.ProgressBar(); // $(document).on('ajax:send', function(event){ diff --git a/app/assets/javascripts/admins/common-refuse-modal.js b/app/assets/javascripts/admins/common-refuse-modal.js index 5eb2f3f46..4e1af891e 100644 --- a/app/assets/javascripts/admins/common-refuse-modal.js +++ b/app/assets/javascripts/admins/common-refuse-modal.js @@ -27,10 +27,11 @@ $(document).on('turbolinks:load', function() { }); // modal visited fire $refuseModal.on('shown.bs.modal', function(){ - $refuseModal.find('.modal-body input[name="reason"]').focus(); + $refuseModal.find('.modal-body textarea[name="reason"]').focus(); }); $refuseModal.on('hide.bs.modal', function () { $applyIdInput.val(''); + $refuseModal.find('.modal-body textarea[name="reason"]').val(''); $form.data('url', ''); }) diff --git a/app/assets/javascripts/admins/dashboards/index.js b/app/assets/javascripts/admins/dashboards/index.js new file mode 100644 index 000000000..ab9cde3fc --- /dev/null +++ b/app/assets/javascripts/admins/dashboards/index.js @@ -0,0 +1,66 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-dashboards-index-page').length > 0) { + // 月新增用户 + var monthChart = echarts.init(document.getElementById('month-active-user')); + monthChart.setOption({ + tooltip: { + show: "true", + trigger: 'item', + formatter: '{c0}', + backgroundColor: 'rgba(0,0,0,0.7)', // 背景 + padding: [8, 10], //内边距 + extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影 + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + series : [ + { + name: '访问来源', + type: 'pie', + radius: '55%', + data: [] + } + ] + }); + monthChart.showLoading(); + $.get('/admins/dashboards/month_active_user.json').done(function(data){ + monthChart.setOption({ + series: [ + { data: data.data } + ] + }); + + monthChart.hideLoading(); + }); + + + // 近七天评测次数 + // var evaluateChart = echarts.init(document.getElementById('evaluate-pie')); + // evaluateChart.setOption({ + // tooltip: { + // show: "true", + // trigger: 'item', + // formatter: '{c0}', + // backgroundColor: 'rgba(0,0,0,0.7)', // 背景 + // padding: [8, 10], //内边距 + // extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影 + // axisPointer: { // 坐标轴指示器,坐标轴触发有效 + // type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + // } + // }, + // xAxis: { type: 'category', boundaryGap: false, data: [] }, + // yAxis: { type: 'value' }, + // series: [{ data: [], type: 'line', areaStyle: {} }] + // }); + // evaluateChart.showLoading(); + // $.get('/admins/dashboards/evaluate.json').done(function(data){ + // evaluateChart.setOption({ + // xAxis: { data: data.names }, + // series: [{ data: data.data }] + // }); + // + // evaluateChart.hideLoading(); + // }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/departments/index.js b/app/assets/javascripts/admins/departments/index.js new file mode 100644 index 000000000..eb0fc3a6a --- /dev/null +++ b/app/assets/javascripts/admins/departments/index.js @@ -0,0 +1,173 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-departments-index-page').length > 0) { + var $searchContainer = $('.department-list-form'); + var $searchForm = $searchContainer.find('form.search-form'); + var $list = $('.department-list-container'); + + $searchContainer.on('change', '.form-check-input', function(){ + $searchForm.find('input[type="submit"]').trigger('click'); + }); + + // ============== 新建部门 =============== + var $modal = $('.modal.admin-create-department-modal'); + var $form = $modal.find('form.admin-create-department-form'); + var $departmentNameInput = $form.find('input[name="department_name"]'); + var $schoolSelect = $modal.find('.school-select'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + school_id: { + required: true + }, + department_name: { + required: true + } + }, + messages: { + school_id: { + required: '请选择所属单位' + } + } + }); + + // modal ready fire + $modal.on('show.bs.modal', function () { + $departmentNameInput.val(''); + $schoolSelect.select2('val', ' '); + }); + + // ************** 学校选择 ************* + var matcherFunc = function(params, data){ + if ($.trim(params.term) === '') { + return data; + } + if (typeof data.text === 'undefined') { + return null; + } + + if (data.name && data.name.indexOf(params.term) > -1) { + var modifiedData = $.extend({}, data, true); + return modifiedData; + } + + // Return `null` if the term should not be displayed + return null; + }; + + var defineSchoolSelect = function(schools) { + $schoolSelect.select2({ + theme: 'bootstrap4', + placeholder: '请选择所属单位', + minimumInputLength: 1, + data: schools, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.name; + }, + templateSelection: function(item){ + if (item.id) { + $('#school_id').val(item.id); + } + return item.name || item.text; + }, + matcher: matcherFunc + }); + } + + $.ajax({ + url: '/api/schools/for_option.json', + dataType: 'json', + type: 'GET', + success: function(data) { + defineSchoolSelect(data.schools); + } + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'json', + url: url, + data: $form.serialize(), + success: function(){ + $.notify({ message: '创建成功' }); + $modal.modal('hide'); + + setTimeout(function(){ + window.location.reload(); + }, 500); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + } + }); + } + }); + + // ============= 添加部门管理员 ============== + var $addMemberModal = $('.admin-add-department-member-modal'); + var $addMemberForm = $addMemberModal.find('.admin-add-department-member-form'); + var $memberSelect = $addMemberModal.find('.department-member-select'); + var $departmentIdInput = $addMemberForm.find('input[name="department_id"]') + + $addMemberModal.on('show.bs.modal', function(event){ + var $link = $(event.relatedTarget); + var departmentId = $link.data('department-id'); + $departmentIdInput.val(departmentId); + + $memberSelect.select2('val', ' '); + }); + + $memberSelect.select2({ + theme: 'bootstrap4', + placeholder: '请输入要添加的管理员姓名', + multiple: true, + minimumInputLength: 1, + ajax: { + delay: 500, + url: '/admins/users', + dataType: 'json', + data: function(params){ + return { name: params.term }; + }, + processResults: function(data){ + return { results: data.users } + } + }, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.real_name; + }, + templateSelection: function(item){ + if (item.id) { + } + return item.real_name || item.text; + } + }); + + $addMemberModal.on('click', '.submit-btn', function(){ + $addMemberForm.find('.error').html(''); + + var departmentId = $departmentIdInput.val(); + var memberIds = $memberSelect.val(); + if (departmentId && memberIds && memberIds.length > 0) { + $.ajax({ + method: 'POST', + dataType: 'script', + url: '/admins/departments/' + departmentId + '/department_member', + data: { user_ids: memberIds } + }); + } else { + $addMemberModal.modal('hide'); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/library_applies/index.js b/app/assets/javascripts/admins/library_applies/index.js new file mode 100644 index 000000000..ab0fc35e2 --- /dev/null +++ b/app/assets/javascripts/admins/library_applies/index.js @@ -0,0 +1,20 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-library-applies-index-page').length > 0) { + var $searchFrom = $('.library-applies-list-form'); + $searchFrom.find('select[name="status"]').val('pending'); + + $searchFrom.on('click', '.search-form-tab', function(){ + var $link = $(this); + + $searchFrom.find('input[name="keyword"]').val(''); + $searchFrom.find('select[name="status"]').val('processed'); + + if($link.data('value') === 'processed'){ + $searchFrom.find('.status-filter').show(); + } else { + $searchFrom.find('.status-filter').hide(); + $searchFrom.find('select[name="status"]').val('pending'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/mirror_repositories/edit.js b/app/assets/javascripts/admins/mirror_repositories/edit.js new file mode 100644 index 000000000..7fb3ad10d --- /dev/null +++ b/app/assets/javascripts/admins/mirror_repositories/edit.js @@ -0,0 +1,19 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-mirror-repositories-edit-page, body.admins-mirror-repositories-update-page').length > 0) { + var $form = $('form.edit-mirror'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + "mirror_repository[type_name]": { + required: true + } + } + }); + + $form.submit(function(e){ + if(!$form.valid()){ e.preventDefault(); } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/mirror_repositories/index.js b/app/assets/javascripts/admins/mirror_repositories/index.js new file mode 100644 index 000000000..2e30bdd94 --- /dev/null +++ b/app/assets/javascripts/admins/mirror_repositories/index.js @@ -0,0 +1,4 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-mirror-repositories-index-page').length > 0) { + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/mirror_scripts/form.js b/app/assets/javascripts/admins/mirror_scripts/form.js new file mode 100644 index 000000000..3aa318d8d --- /dev/null +++ b/app/assets/javascripts/admins/mirror_scripts/form.js @@ -0,0 +1,33 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-mirror-scripts-edit-page, body.admins-mirror-scripts-update-page, body.admins-mirror-scripts-new-page, body.admins-mirror-scripts-create-page').length > 0) { + var $form = $('form.script-form'); + + // codemirror编辑器 + var scriptEditor = CodeMirror.fromTextArea(document.getElementById('mirror_script_script'), { + lineNumbers: true, + mode: 'shell', + theme: "default", + indentUnit: 4, //代码缩进为一个tab的距离 + matchBrackets: true, + autoRefresh: true, + smartIndent: true,//智能换行 + styleActiveLine: true, + lint: true + }); + scriptEditor.setSize('auto', '600px'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + "mirror_script[script_type]": { + required: true + } + } + }); + + $form.submit(function(e){ + if(!$form.valid()){ e.preventDefault(); } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-choose-mirror-modal.js b/app/assets/javascripts/admins/modals/admin-choose-mirror-modal.js new file mode 100644 index 000000000..6111b2401 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-choose-mirror-modal.js @@ -0,0 +1,32 @@ +$(document).on('turbolinks:load', function() { + $('.admin-modal-container').on('show.bs.modal', '.modal.admin-choose-mirror-modal', function(){ + var $modal = $('.modal.admin-choose-mirror-modal'); + var $form = $modal.find('form.admin-choose-mirror-form'); + + var validateForm = function(){ + var checkedValue = $form.find('input[name="mirror_number"]:checked').val(); + + if(checkedValue == undefined){ + $modal.find('.error').html('必须选择一种镜像保存!'); + return false; + } + return true; + } + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + var url = $form.attr('action'); + + if (validateForm()) { + $.ajax({ + method: 'POST', + dataType: 'script', + url: url, + data: $form.serialize(), + }).done(function(){ + $modal.modal('hide'); + }); + } + }); + }) +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-edit-department-modal.js b/app/assets/javascripts/admins/modals/admin-edit-department-modal.js new file mode 100644 index 000000000..a1df01ba5 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-edit-department-modal.js @@ -0,0 +1,34 @@ +$(document).on('turbolinks:load', function() { + $('.admin-modal-container').on('show.bs.modal', '.modal.admin-edit-department-modal', function(){ + var $modal = $('.modal.admin-edit-department-modal'); + var $form = $modal.find('form.admin-edit-department-form'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + 'department[name]': { + required: true, + maxlength: 20 + }, + 'department[host_count]': { + digits: true + } + } + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + var url = $form.attr('action'); + + if ($form.valid()) { + $.ajax({ + method: 'PATCH', + dataType: 'script', + url: url, + data: $form.serialize() + }); + } + }); + }) +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-merge-department-modal.js b/app/assets/javascripts/admins/modals/admin-merge-department-modal.js new file mode 100644 index 000000000..aead3f485 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-merge-department-modal.js @@ -0,0 +1,110 @@ +$(document).on('turbolinks:load', function() { + var $modal = $('.modal.admin-merge-department-modal'); + if ($modal.length > 0) { + var $form = $modal.find('form.admin-merge-department-form'); + var $schoolIdInput = $form.find('input[name="school_id"]'); + var $originDepartmentIdInput = $form.find('input[name="origin_department_id"]'); + var $departmentSelect = $modal.find('.department-select'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + department_id: { + required: true + } + }, + messages: { + department_id: { + required: '请选择部门' + } + } + }); + + // ************** 学校选择 ************* + 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 defineDepartmentSelect = function(departments) { + $departmentSelect.empty(); + + $departmentSelect.select2({ + theme: 'bootstrap4', + placeholder: '请选择所属部门', + data: departments, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.name; + }, + templateSelection: function(item){ + if (item.id) { + $form.find('#department_id').val(item.id); + } + return item.name || item.text; + }, + matcher: matcherFunc + }); + $departmentSelect.select2('val', ' '); + }; + + // modal ready fire + $modal.on('show.bs.modal', function (event) { + var $link = $(event.relatedTarget); + + var schoolId = $link.data('schoolId'); + + $schoolIdInput.val(schoolId); + $originDepartmentIdInput.val($link.data('departmentId')); + + $.ajax({ + url: '/api/schools/' + schoolId + '/departments/for_option.json', + dataType: 'json', + type: 'GET', + success: function(data) { + defineDepartmentSelect(data.departments); + } + }); + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'json', + url: url, + data: $form.serialize(), + success: function(){ + $.notify({ message: '操作成功' }); + $modal.modal('hide'); + + setTimeout(function(){ + window.location.reload(); + }, 500); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-replace-mirror-modal.js b/app/assets/javascripts/admins/modals/admin-replace-mirror-modal.js new file mode 100644 index 000000000..8b7a129a6 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-replace-mirror-modal.js @@ -0,0 +1,89 @@ +$(document).on('turbolinks:load', function() { + var $modal = $('.modal.admin-replace-mirror-modal'); + if ($modal.length > 0) { + var $form = $modal.find('form.admin-replace-mirror-form'); + var $mirrorIdInput = $modal.find('.modal-body input[name="mirror_id"]'); + var $mirrorSelect = $modal.find('.new-mirror-select'); + + var setMirror = function(id, name){ + $mirrorIdInput.val(id); + $form.find('.mirror-id-container').html(id); + $form.find('.mirror-name-container').html(name); + } + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + new_mirror_id: { + required: true + }, + }, + messages: { + new_mirror_id: { + required: '请选择新镜像' + } + } + }); + + // modal ready fire + $modal.on('show.bs.modal', function (event) { + var $link = $(event.relatedTarget); + + var mirrorId = $link.data('id'); + var mirrorName = $link.data('name'); + + setMirror(mirrorId, mirrorName); + $mirrorSelect.select2('val', ' '); + }); + $modal.on('hide.bs.modal', function () { + setMirror('', ''); + $mirrorSelect.select2('val', ' '); + $('#new_mirror_id-error').remove(); + }); + + $mirrorSelect.select2({ + theme: 'bootstrap4', + placeholder: '输入要合并的镜像名', + minimumInputLength: 1, + ajax: { + url: '/admins/mirror_repositories/for_select', + dataType: 'json', + data: function(params){ + return { keyword: params.term }; + }, + processResults: function(data){ + return { results: data.mirrors } + } + }, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.name; + }, + templateSelection: function(item){ + if (item.id) { + $('#new_mirror_id-error').remove(); + $('#new_mirror_id').val(item.id); + } + return item.name || item.text; + } + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'script', + url: url, + data: $form.serialize(), + }).done(function(){ + $modal.modal('hide'); + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-upload-file-modal.js b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js new file mode 100644 index 000000000..cf1333381 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js @@ -0,0 +1,62 @@ +$(document).on('turbolinks:load', function() { + var $modal = $('.modal.admin-upload-file-modal'); + if ($modal.length > 0) { + var $form = $modal.find('form.admin-upload-file-form') + var $sourceIdInput = $modal.find('input[name="source_id"]'); + var $sourceTypeInput = $modal.find('input[name="source_type"]'); + + $modal.on('show.bs.modal', function(event){ + var $link = $(event.relatedTarget); + var sourceId = $link.data('sourceId'); + var sourceType = $link.data('sourceType'); + + $sourceIdInput.val(sourceId); + $sourceTypeInput.val(sourceType); + + $modal.find('.upload-file-input').trigger('click'); + }); + + $modal.find('.upload-file-input').on('change', function(e){ + var file = $(this)[0].files[0]; + + if(file){ + $modal.find('.file-names').html(file.name); + $modal.find('.submit-btn').trigger('click'); + } + }) + + var formValid = function(){ + if($form.find('input[name="file"]').val() == undefined || $form.find('input[name="file"]').val().length == 0){ + $form.find('.error').html('请选择文件'); + return false; + } + + return true; + }; + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if (formValid()) { + var formDataString = $form.serialize(); + $.ajax({ + method: 'POST', + dataType: 'json', + url: '/admins/files?' + formDataString, + data: new FormData($form[0]), + processData: false, + contentType: false, + success: function(data){ + $.notify({ message: '上传成功' }); + $modal.trigger('upload:success', data); + $modal.modal('hide'); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/project_package_applies/project_package_applies.js b/app/assets/javascripts/admins/project_package_applies/project_package_applies.js new file mode 100644 index 000000000..153ad1f66 --- /dev/null +++ b/app/assets/javascripts/admins/project_package_applies/project_package_applies.js @@ -0,0 +1,20 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-project-package-applies-index-page').length > 0) { + var $searchFrom = $('.project-package-applies-form'); + $searchFrom.find('select[name="status"]').val('pending'); + + $searchFrom.on('click', '.search-form-tab', function(){ + var $link = $(this); + + $searchFrom.find('input[name="keyword"]').val(''); + $searchFrom.find('select[name="status"]').val('all'); + + if($link.data('value') === 'all'){ + $searchFrom.find('.status-filter').show(); + } else { + $searchFrom.find('.status-filter').hide(); + $searchFrom.find('select[name="status"]').val('pending'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/shixun_settings/index.js b/app/assets/javascripts/admins/shixun_settings/index.js new file mode 100644 index 000000000..f99574673 --- /dev/null +++ b/app/assets/javascripts/admins/shixun_settings/index.js @@ -0,0 +1,44 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-shixun-settings-index-page').length > 0) { + let searchContainer = $(".shixun-settings-list-form"); + let searchForm = $("form.search-form",searchContainer); + + searchContainer.on('change', '.shixun-settings-select', function(){ + searchForm.find('input[type="submit"]').trigger('click'); + }); + + //导出 + searchContainer.on('click',"#shixun-settings-export",function () { + window.location.href = "/admins/shixun_settings.xls?" + searchForm.serialize(); + }); + + $(".shixun-settings-list-container").on("change", '.shixun-setting-form', function () { + var s_id = $(this).attr("data-id"); + var s_value = $(this).val(); + var s_name = $(this).attr("name"); + var json = {}; + var s_index = $(this).parent("td").siblings(".shixun-line-no").text(); + json[s_name] = s_value; + json["page_no"] = s_index; + $.ajax({ + url: "/admins/shixun_settings/" + s_id, + type: "PUT", + dataType:'script', + data: json + }) + }); + + $("select#settings-tag-choosed").select2({ + placeholder: "请选择分类", + allowClear: true + }); + + $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ + var $imageElement = $('.shixun-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + }) + } +}); + diff --git a/app/assets/javascripts/admins/shixuns/index.js b/app/assets/javascripts/admins/shixuns/index.js new file mode 100644 index 000000000..849e13b5b --- /dev/null +++ b/app/assets/javascripts/admins/shixuns/index.js @@ -0,0 +1,14 @@ +$(document).on('turbolinks:load', function() { + if($('body.admins-shixuns-index-page').length > 0){ + $('select#tag-choosed').select2({ + placeholder: "请选择分类", + allowClear: true + }); + + let search_form = $(".search-form"); + //导出 + $(".shixuns-list-form").on("click","#shixuns-export",function () { + window.location.href = "/admins/shixuns.xls?" + search_form.serialize(); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/video_applies/index.js b/app/assets/javascripts/admins/video_applies/index.js new file mode 100644 index 000000000..2e72acd97 --- /dev/null +++ b/app/assets/javascripts/admins/video_applies/index.js @@ -0,0 +1,20 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-video-applies-index-page').length > 0) { + var $searchFrom = $('.video-applies-form'); + $searchFrom.find('select[name="status"]').val('pending'); + + $searchFrom.on('click', '.search-form-tab', function(){ + var $link = $(this); + + $searchFrom.find('input[name="keyword"]').val(''); + $searchFrom.find('select[name="status"]').val('all'); + + if($link.data('value') === 'all'){ + $searchFrom.find('.status-filter').show(); + } else { + $searchFrom.find('.status-filter').hide(); + $searchFrom.find('select[name="status"]').val('pending'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/college.js b/app/assets/javascripts/college.js new file mode 100644 index 000000000..8f99236a5 --- /dev/null +++ b/app/assets/javascripts/college.js @@ -0,0 +1,21 @@ +//= require rails-ujs +//= require turbolinks +//= require jquery3 +//= require popper +//= require bootstrap-sprockets + +//= require echarts + +//= require_tree ./colleges + +Turbolinks.setProgressBarDelay(200); + +$(document).on('turbolinks:load', function() { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="popover"]').popover(); +}) + +$(document).on("turbolinks:before-cache", function () { + $('[data-toggle="tooltip"]').tooltip('hide'); + $('[data-toggle="popover"]').popover('hide'); +}); \ No newline at end of file diff --git a/app/assets/javascripts/colleges/statistics.js b/app/assets/javascripts/colleges/statistics.js new file mode 100644 index 000000000..6e2790df1 --- /dev/null +++ b/app/assets/javascripts/colleges/statistics.js @@ -0,0 +1,156 @@ +$(document).on('turbolinks:load', function() { + if($('body.colleges-statistics-page').length > 0) { + var $statisticBody = $('.statistics-body'); + var $statisticBase = $('.statistic-base'); + var schoolId = $statisticBody.data('id'); + var $statisticCourse = $statisticBody.find('.statistic-course') + var $shixunChart = $statisticBody.find('.shixun-chart'); + + $.get('/colleges/' + schoolId + '/shixun_time', function(data){ + $statisticBase.find('.shixun-time').html("" + data.shixun_time + "天"); + }); + $.get('/colleges/' + schoolId + '/shixun_report_count', function(data){ + $statisticBase.find('.shixun-report-count').html("" + data.shixun_report_count + "个"); + }); + + $.ajax({ url: '/colleges/' + schoolId + '/course_statistics', method: 'GET', dataType: 'script' }); + $.ajax({ url: '/colleges/' + schoolId + '/teachers', method: 'GET', dataType: 'script' }); + + var initShixunChart = function(names, data){ + var shixunChart = echarts.init(document.getElementById('shixun-chart')); + var options = { + series : [ + { + name: '访问来源', + type: 'pie', + radius: '55%', + data: data + } + ] + }; + + shixunChart.setOption(options); + }; + $.get('/colleges/' + schoolId + '/shixun_chart_data', function(data){ + $statisticBody.find('.shixun-chart-loading').hide(); + if (data.data.length > 0) { + $shixunChart.css('height', '400px').css('width', '100%'); + initShixunChart(data.names, data.data); + } else { + $statisticBody.find('.shixun-chart-empty').show(); + } + }); + + $.ajax({ url: '/colleges/' + schoolId + '/student_shixun', method: 'GET', dataType: 'script' }); + + var initHotEvaluating = function(names, values){ + var Color = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967']; + + var option = { + backgroundColor: '#fff', + grid: { + left: '3%', + right: '4%', + bottom: '10%', + containLabel: true + }, + + tooltip: { + show: "true", + trigger: 'item', + formatter: '{c0}', + backgroundColor: 'rgba(0,0,0,0.7)', // 背景 + padding: [8, 10], //内边距 + extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影 + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + xAxis: { + type: 'value', + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: '#CCCCCC' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#CCCCCC' + } + }, + axisLabel: { + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + }, + formatter: '{value}' + } + }, + yAxis: { + type: 'category', + axisLine: { + lineStyle: { + color: '#cccccc' + } + }, + splitLine: { + show: false + }, + axisTick: { + show: false + }, + splitArea: { + show: false + }, + axisLabel: { + inside: false, + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + } + }, + data: names + }, + series: [{ + name: '', + type: 'bar', + itemStyle: { + normal: { + show: true, + color: function(params) { + return Color[params.dataIndex] + }, + barBorderRadius: 50, + borderWidth: 0, + borderColor: '#333' + } + }, + barGap: '0%', + barCategoryGap: '50%', + data: values + } + + ] + }; + var myChart = echarts.init(document.getElementById('hot-chart')); + myChart.setOption(option); + } + + $.get('/colleges/' + schoolId + '/student_hot_evaluations', function(data){ + $statisticBody.find('.hot-chart-loading').hide(); + if (data.names.length > 0) { + $statisticBody.find('.hot-chart').css('height', '400px').css('width', '100%'); + initHotEvaluating(data.names.reverse(), data.values.reverse()); + } else { + $statisticBody.find('.hot-chart-empty').show(); + } + }) + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/homework_banks.js b/app/assets/javascripts/homework_banks.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/homework_banks.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 0aa1329ca..d3a298dcf 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -5,7 +5,9 @@ @import "select2-bootstrap4.min"; @import "bootstrap-datepicker"; @import "bootstrap-datepicker.standalone"; +@import "lib/codemirror"; +@import "common"; @import "admins/*"; body { @@ -17,20 +19,7 @@ body { align-items: stretch; font-size: 14px; background: #efefef; -} - -a { - &:hover { - text-decoration: unset; - } -} - -textarea.danger, input.danger { - border-color: #dc3545!important; -} - -label.error { - color: #dc3545!important; + overflow: hidden; } .simple_form { @@ -45,6 +34,16 @@ label.error { } } -.flex-1 { - flex: 1; -} \ No newline at end of file +input.form-control { + font-size: 14px; +} + +.btn-default{ + color: #666; + background: #e1e1e1!important; +} +.export-absolute{ + right:20px; + position: absolute; +} +.position-r{position:relative;} diff --git a/app/assets/stylesheets/admins/common.scss b/app/assets/stylesheets/admins/common.scss index 2254166fd..56b286981 100644 --- a/app/assets/stylesheets/admins/common.scss +++ b/app/assets/stylesheets/admins/common.scss @@ -106,5 +106,9 @@ padding: 0.5rem 2rem; } } + + .CodeMirror { + border: 1px solid #ced4da; + } } diff --git a/app/assets/stylesheets/admins/dashboards.scss b/app/assets/stylesheets/admins/dashboards.scss new file mode 100644 index 000000000..d401b7f62 --- /dev/null +++ b/app/assets/stylesheets/admins/dashboards.scss @@ -0,0 +1,7 @@ +.admins-dashboards-index-page { + .pie-statistic { + .pie { + height: 300px; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/departments.scss b/app/assets/stylesheets/admins/departments.scss new file mode 100644 index 000000000..7d9d078e5 --- /dev/null +++ b/app/assets/stylesheets/admins/departments.scss @@ -0,0 +1,24 @@ +.admins-departments-index-page { + .department-list-table { + .member-container { + .member-user { + display: flex; + justify-content: center; + flex-wrap: wrap; + + .member-user-item { + display: flex; + align-items: center; + height: 22px; + line-height: 22px; + padding: 2px 5px; + margin: 2px 2px; + border: 1px solid #91D5FF; + background-color: #E6F7FF; + color: #91D5FF; + border-radius: 4px; + } + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/library_applies.scss b/app/assets/stylesheets/admins/library_applies.scss new file mode 100644 index 000000000..23b7ce30a --- /dev/null +++ b/app/assets/stylesheets/admins/library_applies.scss @@ -0,0 +1,9 @@ +.admins-library-applies-index-page { + .library-applies-list-container { + span { + &.apply-status-agreed { color: #28a745; } + &.apply-status-refused { color: #dc3545; } + &.apply-status-processed { color: #6c757d; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/project_package_apply.scss b/app/assets/stylesheets/admins/project_package_apply.scss new file mode 100644 index 000000000..a6daac205 --- /dev/null +++ b/app/assets/stylesheets/admins/project_package_apply.scss @@ -0,0 +1,9 @@ +.admins-project-package-applies-index-page { + .project-package-applies-list-container { + span { + &.apply-status-agreed { color: #28a745; } + &.apply-status-refused { color: #dc3545; } + &.apply-status-processed { color: #6c757d; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/shixun.scss b/app/assets/stylesheets/admins/shixun.scss new file mode 100644 index 000000000..75f8a54b5 --- /dev/null +++ b/app/assets/stylesheets/admins/shixun.scss @@ -0,0 +1,7 @@ +.admins-shixuns-index-page{ + .shixuns-list-container{ + .shixuns-status-1 { color: #6c757d; } + .shixuns-status-2 { color: #28a745; } + .shixuns-status-3 { color: #dc3545; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/shixun_settings.scss b/app/assets/stylesheets/admins/shixun_settings.scss new file mode 100644 index 000000000..dcfc35650 --- /dev/null +++ b/app/assets/stylesheets/admins/shixun_settings.scss @@ -0,0 +1,22 @@ +.admins-shixun-settings-index-page { + input[type="checkbox"]{ + font-size:18px; + } + .select2 input::-webkit-input-placeholder{ + color:#ccc; + } + .select2 .select2-selection__choice{ + border: 1px solid #eee !important; + } + .setting-chosen{ + font-weight: 400; + font-size: 10px; + color:#333; + } + + .shixun-setting-image { + display: flex; + flex-direction: column; + align-items: center; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/video_apply.scss b/app/assets/stylesheets/admins/video_apply.scss new file mode 100644 index 000000000..993ea1b8f --- /dev/null +++ b/app/assets/stylesheets/admins/video_apply.scss @@ -0,0 +1,9 @@ +.admins-video-applies-index-page { + .video-applies-list-container { + span { + &.apply-status-agreed { color: #28a745; } + &.apply-status-refused { color: #dc3545; } + &.apply-status-processed { color: #6c757d; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/college.scss b/app/assets/stylesheets/college.scss new file mode 100644 index 000000000..8e0ca0e10 --- /dev/null +++ b/app/assets/stylesheets/college.scss @@ -0,0 +1,13 @@ +@import "bootstrap"; +@import "font-awesome-sprockets"; +@import "font-awesome"; + +@import "common"; + +@import "colleges/*"; + + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 1); + font-size: 16px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/colleges/statistic.scss b/app/assets/stylesheets/colleges/statistic.scss new file mode 100644 index 000000000..c3e63e845 --- /dev/null +++ b/app/assets/stylesheets/colleges/statistic.scss @@ -0,0 +1,135 @@ +.colleges-statistics-page { + .college-body-container { + .statistic-header { + width: 100%; + height: 240px; + background-image: url('/images/educoder/statistics.jpg'); + background-size: 100% 100%; + + &-container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + &-title { + flex: 1; + display: flex; + align-items: center; + color: #4CACFF; + font-size: 32px; + } + + &-content { + width: 100%; + display: flex; + justify-content: space-around; + } + + &-item { + margin-bottom: 22px; + display: flex; + flex-direction: column; + align-items: center; + color: #fff; + + &-label { + color: #989898; + } + + &-content { + font-size: 24px; + } + } + } + + .statistic-box { + border: unset; + box-shadow: 0px 0px 9px rgba(174, 175, 177, 0.2); + } + + .statistic-base { + &-title { + padding: 2rem 1.25rem; + background: #fff; + border-bottom: unset; + } + + &-table { + margin: 0; + padding: 0; + } + + &-item { + padding: 0; + + &-label { + text-align: center; + font-size: 16px; + height: 48px; + line-height: 48px; + color: #686868; + background: #F5F5F5; + border-top: 1px solid #EBEBEB; + border-bottom: 1px solid #EBEBEB; + } + + &-content { + height: 100px; + font-size: 16px; + text-align: center; + line-height: 100px; + + span { + margin-right: 5px; + font-size: 24px; + } + } + } + } + + .statistic-container { + padding: 0; + background: #fff; + border-radius: 3px; + box-shadow: 0px 0px 9px rgba(174, 175, 177, 0.2); + + .statistic-label { + padding: 2rem 1.25rem; + font-size: 1.5rem; + } + + .statistic-table { + overflow-x: scroll; + table.course-table { min-width: 1100px; } + table.teacher-rank-table { min-width: 640px; } + } + + table th { + background: #F5F5F5; + border-color: #EBEBEB; + } + + &.statistic-course { + min-height: 400px; + } + + &.statistic-teacher-rank, &.statistic-student-rank { + min-height: 500px; + } + } + + .statistic-chart { + padding: 0 20px; + height: 400px; + + .shixun-chart-loading, .shixun-chart-empty, .hot-chart-loading, .hot-chart-empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss new file mode 100644 index 000000000..3310ec828 --- /dev/null +++ b/app/assets/stylesheets/common.scss @@ -0,0 +1,36 @@ +body { + font-size: 14px; + background: #efefef; +} + +a { + &:hover { + text-decoration: unset; + } +} + +textarea.danger, input.danger { + border-color: #dc3545!important; +} + +label.error { + color: #dc3545!important; +} + +input.form-control { + font-size: 14px; +} +.input-group-prepend { + .input-group-text { + font-size: 14px; + } +} + +.flex-1 { + flex: 1; +} + +.font-12 { font-size: 12px !important; } +.font-14 { font-size: 14px !important; } +.font-16 { font-size: 16px !important; } +.font-18 { font-size: 18px !important; } \ No newline at end of file diff --git a/app/assets/stylesheets/homework_banks.scss b/app/assets/stylesheets/homework_banks.scss new file mode 100644 index 000000000..0cfbbd32d --- /dev/null +++ b/app/assets/stylesheets/homework_banks.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the homework_banks controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/select2-bootstrap4.min.scss b/app/assets/stylesheets/select2-bootstrap4.min.scss index f84b4090a..c39ca0195 100644 --- a/app/assets/stylesheets/select2-bootstrap4.min.scss +++ b/app/assets/stylesheets/select2-bootstrap4.min.scss @@ -1 +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} \ No newline at end of file +.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:#ccc;color:#f8f9fa;float:right;margin-right:.3em}.select2-container--bootstrap4 .select2-selection__clear:hover{background-color:#343a40} \ No newline at end of file diff --git a/app/controllers/admins/choose_mirror_repositories_controller.rb b/app/controllers/admins/choose_mirror_repositories_controller.rb new file mode 100644 index 000000000..c178e0d76 --- /dev/null +++ b/app/controllers/admins/choose_mirror_repositories_controller.rb @@ -0,0 +1,11 @@ +class Admins::ChooseMirrorRepositoriesController < Admins::BaseController + def new + @mirror = MirrorRepository.find(params[:mirror_id]) + @new_mirror = MirrorOperationRecord.where(mirror_repository_id: @mirror.id, status: 1, user_id: -1).first + end + + def create + mirror = MirrorRepository.find(params[:mirror_id]) + Admins::ChooseMirrorService.call(mirror, current_user, params[:mirror_number]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/dashboards_controller.rb b/app/controllers/admins/dashboards_controller.rb index 5d5d6b184..3971971ff 100644 --- a/app/controllers/admins/dashboards_controller.rb +++ b/app/controllers/admins/dashboards_controller.rb @@ -1,4 +1,51 @@ class Admins::DashboardsController < Admins::BaseController def index + @active_user_count = User.where(last_login_on: today).count + @weekly_active_user_count = User.where(last_login_on: current_week).count + @month_active_user_count = User.where(last_login_on: current_month).count + + @new_user_count = User.where(created_on: current_month).count + end + + def month_active_user + count = UserExtension.where(created_at: current_month).group(:identity).count + + data = [ + { value: count['teacher'].to_i, name: '老师' }, + { value: count['student'].to_i, name: '学生' }, + { value: count['professional'].to_i, name: '专业人士' }, + { value: count[nil].to_i, name: '未选职业' }, + ] + + render_ok(data: data) + end + + def evaluate + names = [] + data = [] + + 1.upto(7) do |i| + date = i.days.ago + names.unshift(date.strftime('%Y-%m-%d')) + + count = Output.where(created_at: date.beginning_of_day..date.end_of_day).count + data.unshift(count) + end + + render_ok(names: names, data: data) + end + + private + + def today + Time.now.beginning_of_day..Time.now.end_of_day + end + + def current_week + 7.days.ago.beginning_of_day..Time.now.end_of_day + end + + def current_month + 30.days.ago.beginning_of_day..Time.now.end_of_day end end \ No newline at end of file diff --git a/app/controllers/admins/department_members_controller.rb b/app/controllers/admins/department_members_controller.rb new file mode 100644 index 000000000..ba483acef --- /dev/null +++ b/app/controllers/admins/department_members_controller.rb @@ -0,0 +1,20 @@ +class Admins::DepartmentMembersController < Admins::BaseController + + helper_method :current_department + + def create + Admins::AddDepartmentMemberService.call(current_department, params) + current_department.reload + end + + def destroy + @member = current_department.department_members.find_by(user_id: params[:user_id]) + @member.destroy! if @member.present? + end + + private + + def current_department + @_current_department ||= Department.find(params[:department_id]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/departments_controller.rb b/app/controllers/admins/departments_controller.rb new file mode 100644 index 000000000..ed7c3d3db --- /dev/null +++ b/app/controllers/admins/departments_controller.rb @@ -0,0 +1,95 @@ +class Admins::DepartmentsController < Admins::BaseController + + helper_method :current_department + + def index + params[:sort_by] ||= 'created_at' + params[:sort_direction] ||= 'desc' + + departments = Admins::DepartmentQuery.call(params) + + @departments = paginate departments.preload(:school, :member_users) + + department_ids = @departments.map(&:id) + @users_count = UserExtension.where(department_id: department_ids).group(:department_id).count + @professional_auth_count = UserExtension.where(department_id: department_ids) + .joins(:user).where(users: { professional_certification: true }) + .group(:department_id).count + end + + def create + department_name = params[:department_name].to_s.strip + school = School.find(params[:school_id]) + + return render_error('部门名称重复') if school.departments.exists?(name: department_name) + + ActiveRecord::Base.transaction do + department = school.departments.create!(name: department_name, is_auth: 1) + ApplyAddDepartment.create!(school_id: school.id, status: 1, name: department.name, + department_id: department.id, user_id: current_user.id) + end + + render_ok + end + + def edit + end + + def update + identifier = update_params.delete(:identifier).presence + if identifier && Department.where.not(id: current_department.id).exists?(identifier: identifier) + return render_error('统计链接重复', type: :notify) + end + + current_department.update!(update_params.merge(identifier: identifier)) + end + + def destroy + ActiveRecord::Base.transaction do + current_department.apply_add_departments.update_all(status: 2) + + user_ids = current_department.user_extensions.pluck(:user_id) + if user_ids.present? + DeleteDepartmentNotifyJob.perform_later(current_department.id, 0, user_ids) + current_department.soft_delete! + else + current_department.destroy! + end + end + + render_delete_success + end + + def merge + return render_error('请选择其它部门') if params[:origin_department_id].to_s == params[:department_id].to_s + + origin_department = Department.find(params[:origin_department_id]) + to_department = Department.find(params[:department_id]) + + return render_error('部门所属单位不相同') if origin_department.school_id != to_department.school_id + + ActiveRecord::Base.transaction do + origin_department.apply_add_departments.delete_all + + origin_department.user_extensions.update_all(department_id: to_department.id) + + if to_department.identifier.blank? && origin_department.identifier.present? + to_department.update!(identifier: origin_department.identifier) + end + + origin_department.destroy! + end + + render_ok + end + + private + + def current_department + @_current_department ||= Department.find(params[:id]) + end + + def update_params + params.require(:department).permit(:name, :identifier, :host_count) + end +end \ No newline at end of file diff --git a/app/controllers/admins/files_controller.rb b/app/controllers/admins/files_controller.rb new file mode 100644 index 000000000..3c799ceba --- /dev/null +++ b/app/controllers/admins/files_controller.rb @@ -0,0 +1,54 @@ +class Admins::FilesController < Admins::BaseController + before_action :convert_file!, only: [:create] + + def create + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + + Util.write_file(@file, file_path) + + render_ok(source_id: params[:source_id], source_type: params[:source_type].to_s, url: file_url) + rescue StandardError => ex + logger_error(ex) + render_error('上传失败') + end + + private + + def convert_file! + max_size = 10 * 1024 * 1024 # 10M + if params[:file].class == ActionDispatch::Http::UploadedFile + @file = params[:file] + render_error('请上传文件') if @file.size.zero? + render_error('文件大小超过限制') if @file.size > max_size + else + file = params[:file].to_s.strip + return render_error('请上传正确的图片') if file.blank? + @file = Util.convert_base64_image(file, max_size: max_size) + end + rescue Base64ImageConverter::Error => ex + render_error(ex.message) + end + + def file_path + @_file_path ||= begin + case params[:source_type].to_s + when 'Shixun' then + disk_filename('Shixun', params[:source_id]) + else + disk_filename(params[:source_type].to_s, params[:source_id].to_s) + end + end + end + + def disk_filename(type, id) + File.join(storage_path, type.to_s, id.to_s) + end + + def storage_path + @_storage_path ||= File.join(Rails.root, 'public', 'images', 'avatars') + end + + def file_url + File.join('/images/avatars/', params[:source_type].to_s, params[:source_id].to_s) + end +end \ No newline at end of file diff --git a/app/controllers/admins/library_applies_controller.rb b/app/controllers/admins/library_applies_controller.rb new file mode 100644 index 000000000..678dc31ad --- /dev/null +++ b/app/controllers/admins/library_applies_controller.rb @@ -0,0 +1,25 @@ +class Admins::LibraryAppliesController < Admins::BaseController + def index + params[:status] ||= 'pending' + applies = Admins::LibraryApplyQuery.call(params) + + @library_applies = paginate applies.preload(library: :user) + end + + def agree + Libraries::AgreeApplyService.new(current_library_apply, current_user).call + render_success_js + end + + def refuse + Libraries::RefuseApplyService.new(current_library_apply, current_user, reason: params[:reason]).call + + render_success_js + end + + private + + def current_library_apply + @_current_library_apply ||= LibraryApply.find(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/mirror_repositories_controller.rb b/app/controllers/admins/mirror_repositories_controller.rb new file mode 100644 index 000000000..aed8dc09e --- /dev/null +++ b/app/controllers/admins/mirror_repositories_controller.rb @@ -0,0 +1,97 @@ +class Admins::MirrorRepositoriesController < Admins::BaseController + before_action :check_shixun_mirrors!, only: [:index] + + def index + mirrors = MirrorRepository.all + mirrors = mirrors.reorder(status: :desc, main_type: :desc, type_name: :asc) + + @mirrors = paginate mirrors.includes(:mirror_scripts) + @error_mirror_names = MirrorRepository.where(status: 5).pluck(:name) + end + + def new + @mirror = MirrorRepository.new + end + + def create + @mirror = MirrorRepository.new + Admins::SaveMirrorRepositoryService.call(@mirror, current_user, form_params) + + flash[:success] = '保存成功' + redirect_to edit_admins_mirror_repository_path(@mirror) + rescue ActiveRecord::RecordInvalid + flash.now[:danger] = '保存失败' + render 'new' + rescue Admins::SaveMirrorRepositoryService::Error => ex + flash.now[:danger] = ex.message + render 'new' + end + + def edit + @mirror = current_mirror + end + + def update + @mirror = current_mirror + + Admins::SaveMirrorRepositoryService.call(current_mirror, current_user, form_params) + + flash[:success] = '保存成功' + redirect_to edit_admins_mirror_repository_path(current_mirror) + rescue ActiveRecord::RecordInvalid + flash.now[:danger] = '保存失败' + render 'edit' + rescue Admins::SaveMirrorRepositoryService::Error => ex + flash.now[:danger] = ex.message + render 'edit' + end + + def destroy + return render_js_error('该状态下不允许删除') unless current_mirror.deletable? + + current_mirror.destroy! + + render_delete_success + end + + def for_select + mirrors = MirrorRepository.all + + keyword = params[:keyword].to_s.strip + mirrors = mirrors.where('name LIKE ?', "%#{keyword}%") if keyword.present? + + @mirrors = paginate mirrors + + render_ok(count: @mirrors.total_count, mirrors: @mirrors.as_json(only: %i[id name])) + end + + def merge + origin_mirror = MirrorRepository.find(params[:mirror_id]) + mirror = MirrorRepository.find(params[:new_mirror_id]) + + ActiveRecord::Base.transaction do + origin_mirror.update!(name: mirror.name, mirrorID: mirror.mirrorID) + mirror.destroy! + end + end + + private + + def current_mirror + @_current_mirror ||= MirrorRepository.find(params[:id]) + end + + def form_params + columns = %i[type_name main_type time_limit resource_limit cpu_limit memory_limit description status] + params.require(:mirror_repository).permit(*columns) + end + + def check_shixun_mirrors! + return + return unless request.format.html? + + Admins::CheckShixunMirrorsService.call + rescue Admins::CheckShixunMirrorsService::Error => e + internal_server_error(e.message) + end +end diff --git a/app/controllers/admins/mirror_scripts_controller.rb b/app/controllers/admins/mirror_scripts_controller.rb new file mode 100644 index 000000000..9ed3611f4 --- /dev/null +++ b/app/controllers/admins/mirror_scripts_controller.rb @@ -0,0 +1,59 @@ +class Admins::MirrorScriptsController < Admins::BaseController + helper_method :current_mirror + + def index + scripts = current_mirror.mirror_scripts.order(updated_at: :desc) + @scripts = paginate scripts + end + + def new + @script = current_mirror.mirror_scripts.new + end + + def create + @script = current_mirror.mirror_scripts.new(form_params) + + if @script.save + flash[:success] = '保存成功' + redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script) + else + flash[:danger] = '保存失败' + render 'new' + end + end + + def edit + @script = current_script + end + + def update + @script = current_script + + if @script.update(form_params) + flash[:success] = '保存成功' + redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script) + else + flash[:danger] = '保存失败' + render 'edit' + end + end + + def destroy + current_script.destroy! + render_delete_success + end + + private + + def current_script + @_current_script ||= current_mirror.mirror_scripts.find(params[:id]) + end + + def current_mirror + @_current_mirror ||= MirrorRepository.find(params[:mirror_repository_id]) + end + + def form_params + params.require(:mirror_script).permit(:script_type, :description, :script) + end +end \ No newline at end of file diff --git a/app/controllers/admins/myshixuns_controller.rb b/app/controllers/admins/myshixuns_controller.rb new file mode 100644 index 000000000..f70a64554 --- /dev/null +++ b/app/controllers/admins/myshixuns_controller.rb @@ -0,0 +1,14 @@ +class Admins::MyshixunsController < Admins::BaseController + def index + params[:sort_by] = params[:sort_by].presence || 'created_at' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + + myshixuns = Admins::MyshixunQuery.call(params) + + @myshixuns = paginate myshixuns.includes(:last_executable_task, :last_task, shixun: :user, user: { user_extension: :school }) + + myshixun_ids = @myshixuns.map(&:id) + @finish_game_count = Game.where(myshixun_id: myshixun_ids, status: 2).group(:myshixun_id).count + @total_score = Game.where(myshixun_id: myshixun_ids, status: 2).where('final_score > 0').group(:myshixun_id).sum(:final_score) + end +end \ No newline at end of file diff --git a/app/controllers/admins/project_package_applies_controller.rb b/app/controllers/admins/project_package_applies_controller.rb new file mode 100644 index 000000000..12347d30c --- /dev/null +++ b/app/controllers/admins/project_package_applies_controller.rb @@ -0,0 +1,37 @@ +class Admins::ProjectPackageAppliesController < Admins::BaseController + before_action :current_apply,only: [:agree,:refuse] + + def index + params[:status] ||= 'pending' + status = params[:status] + if status == 'all' + status = %w(agreed refused) + end + package_applies = ProjectPackageApply.where(status: status) + keyword = params[:keyword].to_s.strip || "" + if keyword.present? + package_applies = package_applies.joins(:project_package).where("project_packages.title like ?","%#{keyword}%") + end + @package_applies = paginate package_applies.includes(project_package: { creator: :user_extension }) + end + + def agree + ProjectPackages::AgreeApplyService.new(current_apply).call + render_success_js + rescue ProjectPackages::AgreeApplyService::Error => e + render json: { status: -1, message: e.message } + end + + def refuse + ProjectPackages::RefuseApplyService.new(current_apply, reason: params[:reason]).call + render_success_js + rescue ProjectPackages::RefuseApplyService::Error => e + render json: { status: -1, message: e.message } + end + + private + + def current_apply + @_current_apply ||= ProjectPackageApply.find(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/shixun_settings_controller.rb b/app/controllers/admins/shixun_settings_controller.rb new file mode 100644 index 000000000..0ccd5725d --- /dev/null +++ b/app/controllers/admins/shixun_settings_controller.rb @@ -0,0 +1,97 @@ +class Admins::ShixunSettingsController < Admins::BaseController + + def index + params[:sort_by] = params[:sort_by].presence || 'created_on' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + + shixun_settings = Admins::ShixunSettingsQuery.call(params) + @editing_shixuns = shixun_settings.where(status:0).size + @pending_shixuns = shixun_settings.where(status:1).size + @processed_shixuns = shixun_settings.where(status:2).size + @closed_shixuns = shixun_settings.where(status:3).size + + @sort_json = { + can_copy: params[:can_copy].present? ? params[:can_copy] : false, + webssh: params[:webssh].present? ? params[:webssh] : "0", + hidden: params[:hidden].present? ? params[:hidden] : false, + homepage_show: params[:homepage_show].present? ? params[:homepage_show] : false, + task_pass: params[:task_pass].present? ? params[:task_pass] : false, + code_hidden: params[:code_hidden].present? ? params[:code_hidden] : false + } + + @shixuns_type_check = MirrorRepository.pluck(:type_name,:id) + @shixun_tags = TagRepertoire.order("name asc").pluck(:name,:id) + @params_page = params[:page] || 1 + @shixun_settings = paginate shixun_settings.preload(:user,:tag_repertoires) + + respond_to do |format| + format.js + format.html + format.xls{ + filename = "实训详情_#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}.xls" + send_data(shixun_list_xls(shixun_settings), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename)) + } + end + + end + + def update + @shixun = Shixun.find_by(id:params[:id]) + @page_no = params[:page_no] || "1" + @shixun_tags = TagRepertoire.order("name asc").pluck(:name,:id) + tag_ids = params[:tag_repertoires] + if tag_ids.present? + @shixun&.shixun_tag_repertoires.delete_all + tag_repertoire_ids = @shixun&.tag_repertoires&.pluck(:id) + tag_ids.each do |id| + unless tag_repertoire_ids.include?(id) + tag_repertoire = @shixun.shixun_tag_repertoires.new(shixun_id:@shixun.id,tag_repertoire_id:id) + tag_repertoire.save + end + end + else + unless @shixun.update_attributes(setting_params) + redirect_to admins_shixun_settings_path + flash[:danger] = "更新失败" + end + end + end + + private + def shixun_list_xls shixuns + xls_report = StringIO.new + book = Spreadsheet::Workbook.new + sheet1 = book.create_worksheet :name => "sheet" + blue = Spreadsheet::Format.new :color => :blue, :weight => :bold, :size => 10 + sheet1.row(0).default_format = blue + sheet1.row(0).concat(["实训ID","实训名称","技术平台", "Fork源", "实践任务","选择题任务","挑战人数", "通关人数", "状态","创建者", "单位", "职业", "关卡序号","关卡名称","技能标签"]) + count_row = 1 + shixuns.find_each do |shixun| + sheet1[count_row, 0] = shixun.identifier + sheet1[count_row, 1] = shixun.name + sheet1[count_row, 2] = shixun.shixun_main_name + sheet1[count_row, 3] = shixun.fork_identifier + sheet1[count_row, 4] = shixun.challenges.practice_type.count + sheet1[count_row, 5] = shixun.challenges.choose_type.count + sheet1[count_row, 6] = shixun.myshixuns.count + sheet1[count_row, 7] = shixun.myshixuns.finished.count + sheet1[count_row, 8] = shixun.shixun_status + sheet1[count_row, 9] = shixun.owner.show_real_name + sheet1[count_row, 10] = shixun.owner.school_name + sheet1[count_row, 11] = shixun.owner.identity + shixun.challenges.each do |challenge| + sheet1[count_row, 12] = "第#{challenge.position}关" + sheet1[count_row, 13] = challenge.subject + sheet1[count_row, 14] = challenge.tags_show + count_row += 1 + end + count_row += 1 + end + book.write xls_report + xls_report.string + end + + def setting_params + params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:id,tag_repertoires:[]) + end +end diff --git a/app/controllers/admins/shixuns_controller.rb b/app/controllers/admins/shixuns_controller.rb new file mode 100644 index 000000000..a4aa8a044 --- /dev/null +++ b/app/controllers/admins/shixuns_controller.rb @@ -0,0 +1,64 @@ +class Admins::ShixunsController < Admins::BaseController + + def index + params[:sort_by] = params[:sort_by].presence || 'created_on' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + shixuns = Admins::ShixunQuery.call(params) + @editing_shixuns = shixuns.where(status:0).size + @pending_shixuns = shixuns.where(status:1).size + @processed_shixuns = shixuns.where(status:2).size + @closed_shixuns = shixuns.where(status:3).size + @shixuns_type_check = MirrorRepository.pluck(:type_name,:id) + @params_page = params[:page] || 1 + @shixuns = paginate shixuns.preload(:user,:challenges) + + respond_to do |format| + format.js + format.html + format.xls{ + filename = "实训详情_#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}.xls" + send_data(shixun_list_xls(shixuns), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename)) + } + end + end + + def destroy + Shixun.find(params[:id]).destroy! + + render_delete_success + end + + private + def shixun_list_xls shixuns + xls_report = StringIO.new + book = Spreadsheet::Workbook.new + sheet1 = book.create_worksheet :name => "sheet" + blue = Spreadsheet::Format.new :color => :blue, :weight => :bold, :size => 10 + sheet1.row(0).default_format = blue + sheet1.row(0).concat(["实训ID","实训名称","技术平台", "Fork源", "实践任务","选择题任务","挑战人数", "通关人数", "状态","创建者", "单位", "职业", "关卡序号","关卡名称","技能标签"]) + count_row = 1 + shixuns.find_each do |shixun| + sheet1[count_row, 0] = shixun.identifier + sheet1[count_row, 1] = shixun.name + sheet1[count_row, 2] = shixun.shixun_main_name + sheet1[count_row, 3] = shixun.fork_identifier + sheet1[count_row, 4] = shixun.challenges.practice_type.count + sheet1[count_row, 5] = shixun.challenges.choose_type.count + sheet1[count_row, 6] = shixun.myshixuns.count + sheet1[count_row, 7] = shixun.myshixuns.finished.count + sheet1[count_row, 8] = shixun.shixun_status + sheet1[count_row, 9] = shixun.owner.show_real_name + sheet1[count_row, 10] = shixun.owner.school_name + sheet1[count_row, 11] = shixun.owner.identity + shixun.challenges.each do |challenge| + sheet1[count_row, 12] = "第#{challenge.position}关" + sheet1[count_row, 13] = challenge.subject + sheet1[count_row, 14] = challenge.tags_show + count_row += 1 + end + count_row += 1 + end + book.write xls_report + xls_report.string + end +end diff --git a/app/controllers/admins/video_applies_controller.rb b/app/controllers/admins/video_applies_controller.rb new file mode 100644 index 000000000..2aef602a8 --- /dev/null +++ b/app/controllers/admins/video_applies_controller.rb @@ -0,0 +1,41 @@ +class Admins::VideoAppliesController < Admins::BaseController + + def index + params[:status] ||= 'pending' + status = params[:status] + if status == 'all' + status = %w(agreed refused) + end + + applies = VideoApply.where(status: status).order('video_applies.updated_at desc') + + search = params[:keyword].to_s.strip + if search.present? + applies = applies.joins(:video) + .where('videos.title like :search', search: "%#{search}%") + end + + @video_applies = paginate applies.includes(video: { user: :user_extension }) + end + + def agree + Videos::AgreeApplyService.new(current_video_apply, current_user).call + render_success_js + rescue Videos::AgreeApplyService::Error => e + render json: { status: -1, message: e.message } + end + + def refuse + Videos::RefuseApplyService.new(current_video_apply, current_user, reason: params[:reason]).call + render_success_js + rescue Videos::RefuseApplyService::Error => e + render json: { status: -1, message: e.message } + end + + private + + def current_video_apply + @_current_video_apply ||= VideoApply.find(params[:id]) + end +end + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 177eb1a42..6046be5af 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -416,7 +416,7 @@ class ApplicationController < ActionController::Base # 实训主类别列表,自带描述 def shixun_main_type list = [] - mirrors = MirrorRepository.select([:id, :type_name]).published_main_mirror + mirrors = MirrorRepository.select([:id, :type_name, :description]).published_main_mirror mirrors.try(:each) do |mirror| list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description)} end @@ -426,9 +426,9 @@ class ApplicationController < ActionController::Base # 小类别列表 def shixun_small_type list = [] - mirrors = MirrorRepository.select([:id, :type_name]).published_small_mirror + mirrors = MirrorRepository.select([:id, :type_name, :description]).published_small_mirror mirrors.try(:each) do |mirror| - list << {id: mirror.id, type_name: mirror.type_name} + list << {id: mirror.id, type_name: mirror.type_name, description: mirror.description} end list end diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index 0f6532372..8cb68b1d4 100644 --- a/app/controllers/challenges_controller.rb +++ b/app/controllers/challenges_controller.rb @@ -234,18 +234,21 @@ class ChallengesController < ApplicationController # {...}, ...] #} def crud_answer - raise '参考答案不能为空' if params[:challenge_answer].empty? - raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100 - ActiveRecord::Base.transaction do - @challenge.challenge_answers.destroy_all if @challenge.challenge_answers - params[:challenge_answer].each_with_index do |answer, index| - # 内容为空不保存 - next if answer[:contents].blank? - ChallengeAnswer.create(name: answer[:name], contents: answer[:contents], - level: index+1, score: answer[:score], challenge_id: @challenge.id) + if @challenge.challenge_answers && params[:challenge_answer].blank? + @challenge.challenge_answers.destroy_all + else + raise '参考答案不能为空' if params[:challenge_answer].empty? + raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100 + ActiveRecord::Base.transaction do + @challenge.challenge_answers.destroy_all if @challenge.challenge_answers + params[:challenge_answer].each_with_index do |answer, index| + # 内容为空不保存 + next if answer[:contents].blank? + ChallengeAnswer.create(name: answer[:name], contents: answer[:contents], + level: index+1, score: answer[:score], challenge_id: @challenge.id) + end end end - end # 查看参考答案接口 diff --git a/app/controllers/colleges_controller.rb b/app/controllers/colleges_controller.rb new file mode 100644 index 000000000..66d327649 --- /dev/null +++ b/app/controllers/colleges_controller.rb @@ -0,0 +1,171 @@ +class CollegesController < ApplicationController + include Admins::PaginateHelper + + layout 'college' + + before_action :require_login + before_action :check_college_present! + before_action :check_manage_permission! + + helper_method :current_school, :current_college + + def statistics + # 教师、学生总数 + count_statistic = UserExtension.where(school_id: current_school.id) + .select('SUM(IF(identity=0, 1, 0)) AS teachers_count, SUM(IF(identity=1, 1, 0)) AS students_count').first + @teachers_count = count_statistic['teachers_count'] + @students_count = count_statistic['students_count'] + + # 课堂总数 + @courses_count = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309).count + # 实训总数 + @shixuns_count = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id') + .where(user_extensions: { school_id: current_school.id }).count + end + + def shixun_time + time_sum = Game.joins('left join user_extensions on user_extensions.user_id = games.user_id') + .where(user_extensions: { school_id: current_school.id }).sum(:cost_time) + shixun_time_sum = (time_sum / (24 * 60 * 60.0)).ceil + + render json: { shixun_time: shixun_time_sum } + end + + def shixun_report_count + shixun_report_count = StudentWork.where(work_status: [1, 2]).where('myshixun_id != 0') + .joins('left join user_extensions on user_extensions.user_id = student_works.user_id') + .where(user_extensions: { school_id: current_school.id }).count + render json: { shixun_report_count: shixun_report_count } + end + + def teachers + @teachers = User.find_by_sql("SELECT users.id, users.login, users.lastname, users.firstname, users.nickname, IFNULL((SELECT count(shixuns.id) FROM shixuns where shixuns.user_id =users.id group by shixuns.user_id), 0) AS publish_shixun_count, + (SELECT count(c.id) FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) and c.school_id = #{current_school.id} AND m.user_id=users.id AND c.is_delete = 0) as course_count + FROM `users`, user_extensions ue where users.id=ue.user_id and ue.identity=0 and ue.school_id=#{current_school.id} ORDER BY publish_shixun_count desc, course_count desc, id desc LIMIT 10") + # ).order("publish_shixun_count desc, experience desc").limit(10) + @teachers = + @teachers.map do |teacher| + course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) AND m.user_id=#{teacher.id} AND c.is_delete = 0 and c.school_id = #{current_school.id}") + course_count = course_ids.size + homeworks = HomeworkCommon.where(:homework_type => 4, :course_id => course_ids.map(&:id)) + un_shixun_work_count = homeworks.where("publish_time > '#{Time.now}' or publish_time is null").count + shixun_work_count = homeworks.size - un_shixun_work_count + student_count = StudentsForCourse.where(:course_id => course_ids.map(&:id)).count + myshixun_ids = StudentWork.select("myshixun_id").where("homework_common_id in (#{homeworks.map(&:id).join(',').strip == "" ? -1 : homeworks.map(&:id).join(',')}) and myshixun_id is not null") + complete_myshixun = Myshixun.select("id").where(:status => 1, :id => myshixun_ids.map(&:myshixun_id)).size + all_myshixun = Myshixun.select("id").where(:id => myshixun_ids.map(&:myshixun_id)).size + complete_rate = all_myshixun == 0 ? 0 : ((complete_myshixun * 100) / all_myshixun).try(:round, 2).to_f + real_name = teacher.show_real_name + teacher = teacher.attributes.dup.merge({ + real_name: real_name, + course_count: course_count, + shixun_work_count: shixun_work_count, + un_shixun_work_count: un_shixun_work_count, + student_count: student_count, + complete_rate: complete_rate + }).to_json + JSON.parse(teacher) + end + end + + def shixun_chart_data + shixun_ids = HomeworkCommonsShixun.joins(homework_common: :course).where(courses: {school_id: current_school.id, is_delete: 0}).where('courses.id != 1309').pluck('distinct shixun_id') + shixun_count_map = ShixunTagRepertoire.joins(:tag_repertoire).where(shixun_id: shixun_ids).group('tag_repertoires.name').order('count_shixun_id desc').count(:shixun_id) + + names = [] + data = [] + shixun_count_map.each do |name, count| + break if names.size == 9 + + names << name + data << { value: count, name: name } + end + + if shixun_count_map.keys.size > 9 + other_count = shixun_count_map.values[9..-1].reduce(:+) + names << 'Others' + data << { name: 'Others', value: other_count } + end + + render json: { names: names, data: data } + end + + # 在线课堂 + def course_statistics + courses = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309) + + courses = courses.left_joins(practice_homeworks: { student_works: { myshixun: :games } }) + .select('courses.id, courses.name, courses.is_end, sum(games.evaluate_count) evaluating_count') + .group('courses.id').order('is_end asc, evaluating_count desc') + + params[:per_page] = 8 + @courses = paginate courses + + course_ids = @courses.map(&:id) + @student_count = StudentsForCourse.where(course_id: course_ids).group(:course_id).count + @shixun_work_count = HomeworkCommon.where(homework_type: 4, course_id: course_ids).group(:course_id).count + @attachment_count = Attachment.where(container_id: course_ids, container_type: 'Course').group(:container_id).count + @message_count = Message.joins(:board).where(boards: { parent_id: 0, course_id: course_ids }).group('boards.course_id').count + @active_time = CourseActivity.where(course_id: course_ids).group(:course_id).maximum(:created_at) + @exercise_count = Exercise.where(course_id: course_ids).group(:course_id).count + @poll_count = Poll.where(course_id: course_ids).group(:course_id).count + @other_work_count = HomeworkCommon.where(homework_type: [1,3], course_id: course_ids).group(:course_id).count + end + + # 学生实训 + def student_shixun + @students = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).includes(:user_extension).order('experience desc').limit(10) + + student_ids = @students.map(&:id) + @shixun_count = Myshixun.where(user_id: student_ids).group(:user_id).count + @study_shixun_count = Myshixun.where(user_id: student_ids, status: 0).group(:user_id).count + end + + def student_hot_evaluations + games = Game.joins(:myshixun).joins('join shixun_tag_repertoires str on str.shixun_id = myshixuns.shixun_id') + games = games.joins('join tag_repertoires tr on tr.id = str.tag_repertoire_id') + games = games.joins("join user_extensions ue on ue.user_id = myshixuns.user_id and ue.school_id = #{current_school.id}") + evaluate_count_map = games.group('tr.name').reorder('sum_games_evaluate_count desc').limit(10).sum('games.evaluate_count') + + render json: { names: evaluate_count_map.keys, values: evaluate_count_map.values } + end + + private + + def require_login + return if User.current.logged? + + redirect_to "/login?back_url=#{CGI::escape(request.fullpath)}" + end + + def check_college_present! + return if current_college.present? + + redirect_to '/404' + end + + def check_manage_permission! + return if can_manage_college? + + redirect_to '/403' + end + + def can_manage_college? + return true if current_user.admin_or_business? # 超级管理员|运营 + return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员 + return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师 + return true if current_school.customer_id && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id) + + false + end + + def current_school + current_college.is_a?(School) ? current_college : current_college.school + end + + def current_college + @_current_college ||= begin + Department.find_by(identifier: params[:id]) || School.find_by(id: params[:id]) + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/admins/render_helper.rb b/app/controllers/concerns/admins/render_helper.rb index 0ccc16a09..0f136b62d 100644 --- a/app/controllers/concerns/admins/render_helper.rb +++ b/app/controllers/concerns/admins/render_helper.rb @@ -1,36 +1,34 @@ module Admins::RenderHelper extend ActiveSupport::Concern + def render_by_format(hash) + format = request.format.symbol + hash.key?(format) ? hash[format].call : hash[:html].call + end + def render_forbidden - respond_to do |format| - format.html { redirect_to '/403' } - format.json { super } - end + render_by_format(html: -> { current_user&.business? ? render('admins/shared/403') : redirect_to('/403') }, + json: -> { render status: 403, json: { messages: I18n.t('error.forbidden') } } ) 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 + render_by_format(html: -> { render 'admins/shared/404' }, + js: -> { render_js_error('资源未找到') }, + json: -> { render status: 404, json: { message: '资源未找到' } }) 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 + def render_unprocessable_entity(message, type: :alert) + render_by_format(html: -> { render 'admins/shared/422' }, + js: -> { render_js_error(message, type: type) }, + json: -> { render status: 422, json: { message: message } }) 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('系统错误') } - format.json { render status: 500, json: { message: '系统错误' } } - end + def internal_server_error(message = '系统错误') + @message = message + render_by_format(html: -> { render 'admins/shared/500' }, + js: -> { render_js_error(message) }, + json: -> { render status: 500, json: { message: message } }) end def render_js_template(template, **opts) @@ -42,7 +40,11 @@ module Admins::RenderHelper end alias_method :render_success_js, :render_delete_success - def render_js_error(message) - render_js_template 'admins/shared/error', locals: { message: message } + def render_js_error(message, type: :alert) + if type == :notify + render js: "$.notify({ message: '#{message}' },{ type: 'danger', delay: 5000 });" + else + render_js_template 'admins/shared/error', locals: { message: message } + end end end \ No newline at end of file diff --git a/app/controllers/concerns/git_common.rb b/app/controllers/concerns/git_common.rb index 452204419..eab069b8e 100644 --- a/app/controllers/concerns/git_common.rb +++ b/app/controllers/concerns/git_common.rb @@ -10,7 +10,6 @@ module GitCommon # ------------------------ # 版本库目录结构 def repository - logger.info("ssssssseeeeeeee#{params}") begin @repo_url = repo_url @repo_path @trees = GitService.file_tree(repo_path: @repo_path, path: @path) @@ -44,4 +43,17 @@ module GitCommon end end + # 为版本库添加文件 + def add_file + @path, message, content = params[:path].strip, params[:message], params[:content] + author_name, author_email = current_user.real_name, current_user.git_mail + Rails.logger.info(" good repo_name is #{@repo_path}") + @content = GitService.update_file(repo_path: @repo_path, + file_path: @path, + message: message.force_encoding('UTF-8'), + content: content.force_encoding('UTF-8'), + author_name: author_name, + author_email: author_email) + end + end \ No newline at end of file diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index e29fa8d7d..a26b1c23b 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -26,17 +26,19 @@ class CoursesController < ApplicationController :base_info, :get_historical_courses, :create_group_by_importing_file, :attahcment_category_list,:export_member_scores_excel, :duplicate_course, :switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course, - :informs, :update_informs, :new_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course] + :informs, :update_informs, :online_learning, :update_task_position, :tasks_list, + :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs] before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, - :transfer_to_course_group, :delete_from_course, - :search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :add_teacher] + :transfer_to_course_group, :delete_from_course, :export_member_scores_excel, + :search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, + :add_teacher, :export_couser_info, :export_member_act_score] before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin, :set_course_group, :create_group_by_importing_file, :update_informs, :new_informs, :update_task_position, :tasks_list] before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group, - :change_course_teacher, :export_member_scores_excel, :course_group_list, + :change_course_teacher, :course_group_list, :teacher_application_review, :apply_teachers, :delete_course_teacher] before_action :validate_course_name, only: [:create, :update] before_action :find_board, only: :board_list @@ -888,7 +890,7 @@ class CoursesController < ApplicationController name = worksheet.cell(row, 1).to_s if @course.course_groups.where(:name => name).blank? - @course.course_groups << CourseGroup.new(:name => name) + @course.course_groups << CourseGroup.new(:name => name, :position => @course.course_groups_count + 1) group_count += 1 end end @@ -1081,44 +1083,74 @@ class CoursesController < ApplicationController @courses= @courses.page(@page).per(@page_size) end + # 导出课堂信息 + def export_couser_info + if params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + set_export_cookies + course_info_to_xlsx @course + filename_ = "#{current_user.real_name}_#{@course.name}_课堂信息_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_course_info.xlsx.axlsx", + locals: {course_info: @course_info} + end + end + + # 导出活跃度 + def export_member_act_score + search = params[:search] ? "#{params[:search].strip}" : "" #用户名或学生学号id搜索 + group_id = params[:group_id] #分班的班级id + @all_members = @course.students + @all_members = @all_members.where(course_group_id: group_id) unless group_id.blank? + unless search.blank? + @all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{search}%","%#{search}%") + end + + if @all_members.size == 0 + normal_status(-1,"课堂暂时没有学生") + elsif params[:export].present? && params[:export] + normal_status(0,"正在下载中") + else + set_export_cookies + @all_members = student_act_score group_id, search + act_score_to_xlsx(@all_members) + filename_ = "#{current_user.real_name}_#{@course.name}_活跃度_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_act_score.xlsx.axlsx", + locals: {activity_level:@user_activity_level} + end + end + + # 导出学生成绩 def export_member_scores_excel ActiveRecord::Base.transaction do begin + @all_members = @course.students search = params[:search] ? "#{params[:search].strip}" : "" #用户名或学生学号id搜索 group_id = params[:group_id] #分班的班级id - # if group_id && group_id != "0" && group_id != "-1" - # @all_members = @course.students.course_find_by_ids("course_group_id",group_id) - # elsif group_id && group_id == "0" # 未分班 - # @all_members = @course.course_members.ungroup_students - # else - # @all_members = @course.students - # end - # if name.present? - # @all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{name}%","%#{name}%") - # end - - @all_members = student_act_score group_id, search + @all_members = @all_members.where(course_group_id: group_id) unless group_id.blank? + unless search.blank? + @all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{search}%","%#{search}%") + end - @c_homeworks = @course.homework_commons.homework_published.order("homework_commons.publish_time asc, homework_commons.created_at asc") - @c_exercises = @course.exercises.is_exercise_published.order("exercises.publish_time asc, exercises.created_at asc") - # @c_polls = @course.polls.publish_or_not.order("polls.publish_time asc, polls.created_at asc") - @c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc") - if @user_course_identity > Course::ASSISTANT_PROFESSOR - tip_exception(403,"无权限操作") - elsif @all_members.size == 0 + if @all_members.length == 0 normal_status(-1,"课堂暂时没有学生") elsif params[:export].present? && params[:export] - normal_status(0,"正在下载中") + normal_status(0,"正在下载中") else + @c_homeworks = @course.homework_commons.homework_published.order("homework_commons.publish_time asc, homework_commons.created_at asc") + @c_exercises = @course.exercises.is_exercise_published.order("exercises.publish_time asc, exercises.created_at asc") + # @c_polls = @course.polls.publish_or_not.order("polls.publish_time asc, polls.created_at asc") + @c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc") + set_export_cookies member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks) - filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}" + filename_ = "#{current_user.real_name}_#{@course.name}_总成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}" render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx", - locals: {course_info:@course_info, activity_level:@user_activity_level, - course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, + locals: {course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays, exercise_works:@exercise_work_arrays} end + rescue Exception => e uid_logger_error(e.message) tip_exception(e.message) @@ -1206,13 +1238,14 @@ class CoursesController < ApplicationController tip_exception("开始时间不能为空") if params[:start_date].blank? tip_exception("结束时间不能为空") if params[:end_date].blank? tip_exception("结束时间必须晚于开始时间") if strf_date(params[:end_date]) <= strf_date(params[:start_date]) - tip_exception("开始时间和结束时间不能与往期开课时间重叠") if @course.nil? && @subject.max_course_end_date && strf_date(params[:start_date]) <= strf_date(@subject.max_course_end_date) + tip_exception("开始时间和结束时间不能早于往期开课时间") if @course.nil? && @subject.max_course_end_date && strf_date(params[:start_date]) <= strf_date(@subject.max_course_end_date) validate_start_end_date if @course.present? tip_exception("开放课堂必须包含公告栏和在线学习模块") unless params[:course_module_types].include?("announcement") && params[:course_module_types].include?("online_learning") end tip_exception("课堂所属单位不能为空!") if params[:school].blank? tip_exception("请至少添加一个课堂模块") if params[:course_module_types].blank? - @school = School.find_by!(name: params[:school].strip) + @school = School.find_by(name: params[:school].strip) + tip_exception("所属单位不存在") unless @school.present? end def validate_start_end_date @@ -1291,13 +1324,7 @@ class CoursesController < ApplicationController # end def student_act_score group_id, search - sql_select = %Q{SELECT cm.*,( - SELECT SUM(student_works.work_score) - FROM student_works,homework_commons - WHERE student_works.homework_common_id = homework_commons.id - AND homework_commons.course_id = #{@course.id} - AND student_works.user_id = cm.user_id - ) AS score, + sql_select = %Q{SELECT cm.*, (SELECT max(student_id) FROM user_extensions WHERE user_extensions.user_id = cm.user_id) AS student_id, (SELECT count(messages.id) FROM messages join boards on messages.board_id = boards.id WHERE boards.course_id = #{@course.id} AND messages.author_id = cm.user_id and messages.parent_id is null) AS message_num, @@ -1318,52 +1345,25 @@ class CoursesController < ApplicationController FROM course_members cm} if search.present? && group_id.present? sql_select += %Q{ join users on cm.user_id = users.id - joins user_extensions ue on ue.user_id = users.id + join user_extensions ue on ue.user_id = users.id WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id} and - (concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%') ORDER BY score desc} + (concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%')} elsif search.present? sql_select += %Q{ join users on cm.user_id = users.id - joins user_extensions ue on ue.user_id = users.id + join user_extensions ue on ue.user_id = users.id WHERE cm.role = 4 and - (concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%') ORDER BY score desc} + (concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%')} elsif group_id.present? - sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id} ORDER BY score desc} + sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id}} else - sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} ORDER BY score desc} + sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id}} end act_scores = CourseMember.find_by_sql(sql_select) act_scores end - def member_to_xlsx(course,all_members,homeworks,exercises,tasks) - #课堂的作业信息 - shixun_homeworks = homeworks.search_homework_type(4) #全部实训作业 - shixun_titles = shixun_homeworks.pluck(:name) + ["总得分"] - - # 更新实训作业成绩 - shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework| - homework.update_homework_work_score - end - - shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user) - - common_homeworks = homeworks.search_homework_type(1) #全部普通作业 - common_titles = common_homeworks.pluck(:name)+ ["总得分"] - common_homeworks = common_homeworks&.includes(score_student_works: :user) - - group_homeworks = homeworks.search_homework_type(3) #全部分组作业 - group_titles = group_homeworks.pluck(:name)+ ["总得分"] - group_homeworks = group_homeworks&.includes(score_student_works: :user) - - task_titles = tasks.pluck(:name) + ["总得分"] - tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user) - - exercise_titles = exercises.pluck(:exercise_name) + ["总得分"] - exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user) - - total_user_score_array = [] #学生总成绩集合 - + def course_info_to_xlsx course #课堂信息 @course_info = [] course_info_title = "课堂信息概要" @@ -1411,7 +1411,9 @@ class CoursesController < ApplicationController end course_group_info = [course_group_info_head,course_group_info_body] @course_info += [course_info_title,course_main_info,course_group_info] + end + def act_score_to_xlsx all_members #课堂活跃度 @user_activity_level = [] course_user_level = [] @@ -1426,8 +1428,6 @@ class CoursesController < ApplicationController user_stu_id = u.student_id.present? ? (u.student_id.to_s + "\t") : "--" user_school = user.school_name user_course_group = u.course_group_name - user_info_array = [user_login,user_name,user_mail,user_stu_id,user_school,user_course_group] #用户的信息集合 - user_work_scores = [] #课堂活跃度统计 user_homeworks_num = u.homework_num.to_i #完成的作业数 @@ -1446,23 +1446,69 @@ class CoursesController < ApplicationController c_reply_num = user_reply_num user_activity_levels = c_works_num + c_exercise_num + c_poll_num + c_file_num + c_message_num + c_reply_num + user_work_reply_num user_ac_level = { - u_1: user_name, - u_2: user_login, - u_2_1: user_mail, - u_3: user_stu_id, - u_4: user_school, - u_5: user_course_group, - u_6: c_works_num, - u_7: c_exercise_num, - u_8: c_poll_num, - u_9: c_file_num, - u_10: c_message_num, - u_11: c_reply_num, - u_12: user_work_reply_num, - u_13: user_activity_levels + u_1: user_name, + u_2: user_login, + u_2_1: user_mail, + u_3: user_stu_id, + u_4: user_school, + u_5: user_course_group, + u_6: c_works_num, + u_7: c_exercise_num, + u_8: c_poll_num, + u_9: c_file_num, + u_10: c_message_num, + u_11: c_reply_num, + u_12: user_work_reply_num, + u_13: user_activity_levels } course_user_level.push(user_ac_level) + #.课堂活跃度统计的集合 + course_user_level = course_user_level.sort_by {|k| k[:u_13]}.reverse + @user_activity_level = [course_activity_title,user_cell_head,course_user_level] + end + end + + def member_to_xlsx(course,all_members,homeworks,exercises,tasks) + #课堂的作业信息 + shixun_homeworks = homeworks.search_homework_type(4) #全部实训作业 + shixun_titles = shixun_homeworks.pluck(:name) + ["总得分"] + + # 更新实训作业成绩 + shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework| + homework.update_homework_work_score + end + + shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user) + + common_homeworks = homeworks.search_homework_type(1) #全部普通作业 + common_titles = common_homeworks.pluck(:name)+ ["总得分"] + common_homeworks = common_homeworks&.includes(score_student_works: :user) + + group_homeworks = homeworks.search_homework_type(3) #全部分组作业 + group_titles = group_homeworks.pluck(:name)+ ["总得分"] + group_homeworks = group_homeworks&.includes(score_student_works: :user) + + task_titles = tasks.pluck(:name) + ["总得分"] + tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user) + + exercise_titles = exercises.pluck(:exercise_name) + ["总得分"] + exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user) + + total_user_score_array = [] #学生总成绩集合 + + all_members.each do |u| + #用户的基本信息 + user = u.user + user_login = user.login + user_name = user.real_name + user_mail = user.mail + user_stu_id = user.student_id.present? ? (user.student_id.to_s + "\t") : "--" + user_school = user.school_name + user_course_group = u.course_group_name + user_info_array = [user_name,user_login,user_mail,user_stu_id,user_school,user_course_group] #用户的信息集合 + user_work_scores = [] + #学生总成绩 shixun_score = 0.0 # 实训作业的总分 common_score = 0.0 #普通作业的总分 @@ -1560,10 +1606,6 @@ class CoursesController < ApplicationController total_user_score_array.push(user_work_scores) # 全部成员的集合 end - #1.课堂活跃度统计的集合 - course_user_level = course_user_level.sort_by {|k| k[:u_12]}.reverse - @user_activity_level = [course_activity_title,user_cell_head,course_user_level] - #2.学生总成绩的集合 ## 作业标题的集合 course_user_score_title = "学生总成绩" diff --git a/app/controllers/exercise_bank_questions_controller.rb b/app/controllers/exercise_bank_questions_controller.rb new file mode 100644 index 000000000..1fd8ad874 --- /dev/null +++ b/app/controllers/exercise_bank_questions_controller.rb @@ -0,0 +1,418 @@ +class ExerciseBankQuestionsController < ApplicationController + before_action :require_login, :check_auth #用户需登陆 + before_action :get_exercise, only:[:create] #获取试卷 + before_action :get_exercise_question, except: [:create] #获取试卷的问题及试卷 + before_action :bank_admin #是否为老师 + before_action :validate_params, only: [:create, :update] #传入参数的验证 + + def create + ActiveRecord::Base.transaction do + begin + question_options = { + :question_title => params[:question_title], + :question_type => params[:question_type].present? ? params[:question_type].to_i : 0, #默认为单选题 + :question_number => @exercise.exercise_bank_questions.count + 1, + :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0, + :shixun_id => params[:shixun_id].blank? ? "" : params[:shixun_id], + :is_ordered => params[:is_ordered] # 填空题的答案是否为一一对应关系,默认为true即为一一对应 + } + @exercise_question = @exercise.exercise_bank_questions.new(question_options) + #插入问题时,那么从插入的这个id以后的question_num都将要+1 + if params[:insert_id].present? + insert_exercise = @exercise.exercise_bank_questions.find_by(id: params[:insert_id]) + if insert_exercise.present? #如果该问题存在的话,意思是如果是第一题,那么就不存在插入 + ques_num = insert_exercise.question_number.to_i + @exercise_question.question_number = ques_num + 1 #更新了问题的位置 + @exercise.exercise_bank_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1") + end + end + + if @exercise_question.save + #为选择题(包括单选和多选)的时候,创建问题选项 + ques_type = @exercise_question.question_type + if ques_type <= Exercise::MULTIPLE + choices_array = params[:question_choices] + choices_count= choices_array.count + standard_answer = params[:standard_answers] #为数组格式,因为可能会有单选和多选,标准答案,已提前判断不能为空, + standard_answer = standard_answer.uniq.reject(&:blank?) + (1..choices_count).each do |c| + choice = choices_array[c-1] #每一个选项的内容 + choice_option = { + :choice_position => c, + :choice_text => choice.strip + } + question_choices = @exercise_question.exercise_bank_choices.new(choice_option) + question_choices.save + end + #标准答案的存储,如:["1","2","3"..]等,1对应A,2对应B,3对应C。。。 + standard_answer.each do |a| + choice_id = a.to_i + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => choice_id #即为选择的位置参数 + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + if standard_answer.count > 1 && ques_type == Exercise::SINGLE #当标准答案数大于1,且不为多选时,修改为多选 + @exercise_question.update_attribute("question_type",Exercise::MULTIPLE) + elsif standard_answer.count == 1 && ques_type == Exercise::MULTIPLE + @exercise_question.update_attribute("question_type",Exercise::SINGLE) + end + end + elsif ques_type == Exercise::JUDGMENT #这个为判断题 + choices_array = params[:question_choices] #判断的选项,对/错等等 + choices_count= choices_array.count + (1..choices_count).each do |c| + choice = choices_array[c-1] #每一个选项的内容 + choice_option = { + :choice_position => c, + :choice_text => choice.strip + } + question_choices = @exercise_question.exercise_bank_choices.create(choice_option) + question_choices.save + end + standard_answer = params[:standard_answers] #对应选项的id + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => standard_answer.first.to_i + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + elsif ques_type == Exercise::COMPLETION #填空题,每空的参考答案有多个,那么以位置对应 + standard_answer = params[:standard_answers] + standard_answer.each do |a| + null_choice_id = a[:choice_id] + null_choice_text = a[:answer_text] + null_choice_text.each do |n| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => null_choice_id, + :answer_text => n + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + end + elsif ques_type == Exercise::SUBJECTIVE #简答题 + if params[:standard_answers].present? && params[:standard_answers].reject(&:blank?).count > 0 + standard_answer = params[:standard_answers] + standard_answer.each do |a| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :answer_text => a, + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + end + elsif ques_type == Exercise::PRACTICAL #实训题 + shixun = Shixun.find_by(id: params[:shixun_id]) + shixun_scores = params[:question_scores] #试卷有多个的分值有多个分数表,所以为分数的数组 + shixun_name = params[:shixun_name] || shixun.name + question_score = 0 + shixun.challenges.try(:each_with_index) do |challenge,index| + shixun_option = { + :challenge_id => challenge.id, + :shixun_id => shixun.id, + :exercise_bank_question_id => @exercise_question.id, + :position => (index + 1), + :question_score => shixun_scores[index].present? ? shixun_scores[index].to_f.round(1) : 5 + } + ex_shixun_challenge = ExerciseBankShixunChallenge.create(shixun_option) + question_score += ex_shixun_challenge.question_score # 问题的分数,为各个关卡分数的总和 + end + @exercise_question.update_attributes(:question_score => question_score, :shixun_name=> shixun_name) + end + end + normal_status("创建成功") + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + raise ActiveRecord::Rollback + end + end + end + + def update + ActiveRecord::Base.transaction do + begin + # 更新试卷题目的内容 + question_options = { + :question_title => params[:question_title], + :is_ordered => params[:is_ordered], # 填空题的答案是否为一一对应关系,默认为true即为一一对应 + :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0 #不可修改分数 + } + choices_array = params[:question_choices] + stan_answer_params = params[:standard_answers] + standard_answer = stan_answer_params.present? ? stan_answer_params.uniq.reject(&:blank?) : [] + @exercise_question.update_attributes(question_options) + #当选项存在时,可修改选项内容,但是不能更改选项的位置(即不能增删选项) + if choices_array.present? + ex_choices = @exercise_question.exercise_bank_choices + ex_choice_count = ex_choices.count + choice_array_count = choices_array.count + ex_choice_count_array = (1..ex_choice_count).to_a + choice_array_count_array = (1..choice_array_count).to_a + if ex_choice_count > choice_array_count #如果选项有减少的,那么只更新传入的,删除以前的 + choice_array_count_array.each do |c| + choice = choices_array[c-1] #每一个选项的内容 + exercise_choice = @exercise_question.exercise_bank_choices.find_choice_custom("choice_position",(c)) + exercise_choice.update(choice_text:choice) + end + drop_ex_choice = @exercise_question.exercise_bank_choices.left_choice_choose("choice_position",(choice_array_count)) + drop_ex_choice.destroy_all + else + ex_choice_count_array.each do |c| + choice = choices_array[c-1] #每一个选项的内容 + exercise_choice = @exercise_question.exercise_bank_choices.find_choice_custom("choice_position",(c)) + exercise_choice.update(choice_text:choice) + end + new_add_choice = choice_array_count_array - ex_choice_count_array #新传入的需新增 + if new_add_choice.count > 0 + new_add_choice.each do |i| + choice_option = { + :choice_position => i, + :choice_text => choices_array[i-1].strip + } + question_choices = @exercise_question.exercise_bank_choices.new(choice_option) + question_choices.save + end + end + end + end + #试卷未发布时,当标准答案存在时,可修改标准答案内容,可增删标准答案,否则只能修改标准答案,不能增删标准答案 + @exercise_answers_array = @exercise_question.exercise_bank_standard_answers #问卷的全部标准答案 + if standard_answer.present? + if @exercise_question.question_type <= Exercise::JUDGMENT #选择题/判断题,标准答案为一个或多个 + exercise_standard_choices = @exercise_answers_array.pluck(:exercise_bank_choice_id) #问题以前的全部标准答案选项位置 + if exercise_standard_choices.sort != standard_answer.sort #表示答案有更改的 + common_standard_choices = standard_answer & exercise_standard_choices # 传入的标准答案的选项位置和以前的并集,即表示不用做更改的 + old_left_standard_choices = exercise_standard_choices - common_standard_choices # 以前的差集共同的,剩余的表示需要删掉 + new_left_standard_choices = standard_answer - common_standard_choices # 传入的标准答案差集共同的,剩余的表示需要新建 + if old_left_standard_choices.count > 0 + @exercise_answers_array.standard_by_ids(old_left_standard_choices).destroy_all + end + if new_left_standard_choices.count > 0 #新建标准答案 + new_left_standard_choices.each do |s| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => s.to_i #即为选择的位置参数 + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + + end + if standard_answer.count > 1 && @exercise_question.question_type == Exercise::SINGLE #当标准答案数大于1,且不为多选时,修改为多选 + @exercise_question.update_attribute("question_type",Exercise::MULTIPLE) + elsif standard_answer.count == 1 && @exercise_question.question_type == Exercise::MULTIPLE + @exercise_question.update_attribute("question_type",Exercise::SINGLE) + end + end + elsif @exercise_question.question_type == Exercise::COMPLETION #填空题 + old_ex_answer = @exercise_question.exercise_bank_standard_answers #当前问题的全部标准答案 + old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).uniq.sort + new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.uniq.sort + if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时,才会更新标准答案 + new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号 + old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_bank_choice_id).uniq #全部的答案数组序号 + #删除多余的选项 + if old_ex_answer_choice_ids.count > new_ex_answer_choice_ids.count #有减少的填空 + delete_ex_answer_choice_ids = old_ex_answer_choice_ids - new_ex_answer_choice_ids + old_ex_answer.standard_by_ids(delete_ex_answer_choice_ids).destroy_all + end + standard_answer.each do |aa| + null_choice_id = aa[:choice_id] + null_choice_text = aa[:answer_text] + null_choice_text_count = null_choice_text.count #当前传入的答案数量 + null_choice_text_count_array = (1..null_choice_text_count).to_a + + ex_answer_pre = old_ex_answer.standard_by_ids(null_choice_id) #当前问题的全部答案 + ex_answer_pre_count = ex_answer_pre.count + ex_answer_pre_count_array = (1..ex_answer_pre_count).to_a + + if old_ex_answer_choice_ids.include?(null_choice_id) #以前的填空题答案包含有现在的填空序号 + if null_choice_text_count >= ex_answer_pre_count + new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array + ex_answer_pre_count_array.each do |n| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => null_choice_id, + :answer_text => null_choice_text[n-1] + } + ex_answer_pre[n-1].update(standard_option) + end + if new_add_choice.count > 0 #表示有新增的 + new_add_choice.each do |i| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => null_choice_id, + :answer_text => null_choice_text[i-1] + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + end + else + new_delete_choice = ex_answer_pre_count_array - null_choice_text_count_array + null_choice_text.each_with_index do |n,index| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => null_choice_id, + :answer_text => n + } + ex_answer_pre[index].update(standard_option) + end + if new_delete_choice.count > 0 #表示填空题的答案有删减的 + new_delete_choice.each do |d| + ex_answer_pre[d-1].destroy + end + end + end + else + null_choice_text.each do |n| + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :exercise_bank_choice_id => null_choice_id, + :answer_text => n + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + end + end + end + end + end + if @exercise_question.question_type == Exercise::SUBJECTIVE #主观题 + main_standard_answer = standard_answer.present? ? standard_answer.first : nil + if @exercise_answers_array.present? + @exercise_answers_array.first.update_attribute("answer_text",main_standard_answer) + else + standard_option = { + :exercise_bank_question_id => @exercise_question.id, + :answer_text => main_standard_answer, + } + question_standard_answer = ExerciseBankStandardAnswer.new(standard_option) + question_standard_answer.save + end + elsif @exercise_question.question_type == Exercise::PRACTICAL + question_score = 0 + shixun_name = params[:shixun_name] || @exercise_question.shixun_name + @exercise_question.exercise_bank_shixun_challenges.each_with_index do |challenge, index| + challenge.question_score = params[:question_scores][index].to_f.round(1) + challenge.save + question_score += params[:question_scores][index].to_f.round(1) + end + @exercise_question.question_score = question_score + @exercise_question.shixun_name = shixun_name + @exercise_question.save + end + normal_status(0,"试卷更新成功") + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + raise ActiveRecord::Rollback + end + end + end + + def up_down + ActiveRecord::Base.transaction do + begin + opr = params[:opr] + current_q_p = @exercise_question.question_number.to_i #问题的当前位置 + if opr.present? + if opr.to_s == "up" + last_q_p = @exercise.exercise_bank_questions.find_by(question_number: (current_q_p - 1)) # 当前问题的前一个问题 + if last_q_p.present? + @exercise_question.update_attribute('question_number', (current_q_p - 1)) + last_q_p.update_attribute('question_number', current_q_p) # 重新获取当前问题的位置 + normal_status(0, "问题上移成功!") + else + normal_status(-1, "移动失败,已经是第一个问题了!") + end + elsif opr.to_s == "down" + next_q_p = @exercise.exercise_bank_questions.find_by(question_number: (current_q_p + 1)) # 当前问题的前一个问题 + if next_q_p.present? + @exercise_question.update_attribute('question_number', (current_q_p + 1)) + next_q_p.update_attribute('question_number', current_q_p) + normal_status(0, "问题下移成功!") + else + normal_status(-1, "移动失败,已经是最后一个问题了!") + end + end + else + normal_status(-1, "移动失败,请输入参数") + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + end + end + + #试卷问题的删除 + def destroy + ActiveRecord::Base.transaction do + begin + question_d_id = @exercise_question.question_number.to_i #问题的当前位置 + exercise_questions = @exercise.exercise_bank_questions + left_questions = exercise_questions.where("question_number > ?", question_d_id) + left_questions.update_all("question_number = question_number - 1") if left_questions + @exercise_question.destroy + normal_status(0, "删除成功") + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + end + end + + private + + def bank_admin + tip_exception(403, "无权限") unless @exercise.user_id == current_user.id || current_user.admin? + end + + def get_exercise + @exercise = ExerciseBank.find_by!(id:params[:exercise_bank_id]) + end + + def get_exercise_question + @exercise_question = ExerciseBankQuestion.find_by!(id: params[:id]) + @exercise = @exercise_question.exercise_bank + end + + def validate_params + normal_status(-1,"题目不允许为空!") if (params[:question_title].blank? && params[:question_type].to_i != Exercise::PRACTICAL ) #除了实训题,其余题目必需有题干 + normal_status(-1,"问题类型不允许为空!" ) if params[:question_type].blank? + normal_status(-1,"分值不允许为空!" ) if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个 + if params[:question_score].present? && params[:question_score].to_f <= 0.0 #问题类型存在,则分值不能为空,且必需大于0 + normal_status(-1,"分值必需大于0!") + elsif (params[:question_score].present? && params[:question_score].to_f.round(1) > 100.0) || (params[:question_scores].present? && (params[:question_scores].map{|a| a.to_f.round(1)}.max > 100.0)) + normal_status(-1,"分值不能超过100分!") + elsif params[:question_scores].present? && params[:question_scores].include?(0.0) #如果有负数,则自动取绝对值,#多个分数值,针对实训题的 + normal_status(-1,"分值必需大于0!") + elsif params[:standard_answers].present? && params[:question_choices].present? && (params[:standard_answers].count > params[:question_choices].count) + normal_status(-1,"标准答案数不能大于选项数!") + elsif [0,1,2,3].include?(params[:question_type].to_i) && (params[:standard_answers].blank? || params[:standard_answers].include?("")) #选择题/判断题/填空题 问题选项/标准答案不能为空,也不能包含空的内容 + normal_status(-1,"标准答案不能为空!") + elsif params[:question_type].to_i == 2 && (params[:standard_answers].count > 1 || params[:question_choices].blank? || params[:question_choices].include?("")) #判断题的标准答案不能大于1个,选项不能为空 + normal_status(-1,"判断题选项不能为空/标准答案不能大于1个!") + elsif params[:question_type].to_i <= 1 && (params[:question_choices].blank? || params[:question_choices].include?("") || params[:question_choices].count < 2) #选择题选项不能为空,且不能小于2 + normal_status(-1,"选择题选项内容不能为空,且不能少于2个!") + elsif params[:question_type].to_i == 3 && (params[:standard_answers].blank? || params[:standard_answers].count > 5 ) #填空题选项最多为5个,且如果为1个的话,不允许修改is_ordered + normal_status(-1,"填空题标准答案不能为空/不能超过5个!") + elsif params[:question_type].to_i == 4 && params[:standard_answers].count > 2 #简单题参考答案最多为1个 + normal_status(-1,"简答题的参考答案不能大于2个!") + elsif params[:question_type].to_i == 5 + if params[:shixun_id].blank? #实训题的id不能为空 + normal_status(-1,"实训题id不能为空!") + elsif params[:shixun_name].blank? + normal_status(-1,"实训题名称不能为空!") + end + end + end + +end diff --git a/app/controllers/exercise_banks_controller.rb b/app/controllers/exercise_banks_controller.rb new file mode 100644 index 000000000..dafaf131e --- /dev/null +++ b/app/controllers/exercise_banks_controller.rb @@ -0,0 +1,129 @@ +#encoding: UTF-8 +class ExerciseBanksController < ApplicationController + before_action :require_login + before_action :find_bank, except: [:choose_shixun] + before_action :bank_admin, only: [:update] + before_action :commit_shixun_present, only: [:commit_shixun] + + def show + @exercise_questions = @bank.exercise_bank_questions&.includes(:exercise_bank_choices, :exercise_bank_shixun_challenges, + :exercise_bank_standard_answers).order("question_number ASC") + + if @bank.container_type == "Exercise" + get_exercise_question_count + else + get_poll_question_count + end + end + + def update + tip_exception("标题不能为空!") if params[:exercise_name].blank? + @bank.update_attributes!(name: params[:exercise_name], description: params[:exercise_description]) + normal_status(0,"更新成功") + end + + def choose_shixun + search = params[:search] + type = params[:type] + # 超级管理员用户显示所有未隐藏的实训、非管理员显示所有已发布的实训(对本单位公开且未隐藏未关闭) + if current_user.admin? + @shixuns = Shixun.unhidden + else + none_shixun_ids = ShixunSchool.where("school_id != #{current_user.school_id}").pluck(:shixun_id) + + @shixuns = Shixun.where.not(id: none_shixun_ids).unhidden + end + + # 实训的所有标签 + @tags = TagRepertoire.select([:id, :name]).joins(:shixuns).where(shixuns: {id: @shixuns}).distinct + + if params[:search] && params[:search].strip != "" + @shixuns = @shixuns.joins(:user).where("shixuns.name like ? or concat(users.lastname, users.firstname) like ?", + "%#{search}%", "%#{search}%").distinct + end + + unless type.blank? || type == "all" + @shixuns = @shixuns.joins(:shixun_tag_repertoires).where(shixun_tag_repertoires: {tag_repertoire_id: type}).distinct + end + + @shixuns = @shixuns.select([:id, :name, :status, :myshixuns_count, :identifier, :user_id, :trainee]) + @total_count = @shixuns.size + + ## 分页参数 + page = params[:page] || 1 + @shixuns = @shixuns.reorder("shixuns.created_at desc").includes(:challenges, user: [user_extension: :school]).page(page).per(10) + end + + #确认实训的选择 + def commit_shixun + @shixun_challenges = @shixun.challenges + @shixun_challenges_count = @shixun_challenges.size + end + + private + + def find_bank + @bank = ExerciseBank.find_by!(id: params[:id]) + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? || + (current_user.certification_teacher? && @bank.is_public) + end + + def bank_admin + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? + end + + #判断实训是否已选择 + def commit_shixun_present + question_shixun_ids = @exercise.exercise_bank_questions.pluck(:shixun_id).reject(&:blank?) + shixun_id = params[:shixun_id] + @shixun = Shixun.find_by(id: shixun_id) + if shixun_id.present? && question_shixun_ids.include?(shixun_id) + normal_status(-1,"该实训已选择!") + elsif @shixun.blank? + normal_status(-1,"该实训不存在!") + end + end + + def get_exercise_question_count + @exercise_ques_count = @exercise_questions.size # 全部的题目数 + @exercise_ques_scores = @exercise_questions.pluck(:question_score).sum + + #单选题的数量及分数 + exercise_single_ques = @exercise_questions.find_by_custom("question_type", Exercise::SINGLE) + @exercise_single_ques_count = exercise_single_ques.size + @exercise_single_ques_scores = exercise_single_ques.pluck(:question_score).sum + + #多选题的数量及分数 + exercise_double_ques = @exercise_questions.find_by_custom("question_type", Exercise::MULTIPLE) + @exercise_double_ques_count = exercise_double_ques.size + @exercise_double_ques_scores = exercise_double_ques.pluck(:question_score).sum + + # 判断题数量及分数 + exercise_ques_judge = @exercise_questions.find_by_custom("question_type", Exercise::JUDGMENT) + @exercise_ques_judge_count = exercise_ques_judge.size + @exercise_ques_judge_scores = exercise_ques_judge.pluck(:question_score).sum + + #填空题数量及分数 + exercise_ques_null = @exercise_questions.find_by_custom("question_type", Exercise::COMPLETION) + @exercise_ques_null_count = exercise_ques_null.size + @exercise_ques_null_scores = exercise_ques_null.pluck(:question_score).sum + + #简答题数量及分数 + exercise_ques_main = @exercise_questions.find_by_custom("question_type", Exercise::SUBJECTIVE) + @exercise_ques_main_count = exercise_ques_main.size + @exercise_ques_main_scores = exercise_ques_main.pluck(:question_score).sum + + #实训题数量及分数 + exercise_ques_shixun = @exercise_questions.find_by_custom("question_type", Exercise::PRACTICAL) + @exercise_ques_shixun_count = exercise_ques_shixun.size + @exercise_ques_shixun_scores = exercise_ques_shixun.pluck(:question_score).sum + end + + def get_poll_question_count + @poll_questions_count = @exercise_questions&.size # 全部的题目数 + @poll_question_singles = @exercise_questions.find_by_custom("question_type", 1).size # 单选题 + @poll_question_doubles = @exercise_questions.find_by_custom("question_type", 2).size # 多选题 + @poll_question_mains = @exercise_questions.find_by_custom("question_type", 3).size #主观题 + end + +end diff --git a/app/controllers/exercise_questions_controller.rb b/app/controllers/exercise_questions_controller.rb index e6f17778a..57116a9e5 100644 --- a/app/controllers/exercise_questions_controller.rb +++ b/app/controllers/exercise_questions_controller.rb @@ -255,8 +255,8 @@ class ExerciseQuestionsController < ApplicationController end elsif @exercise_question.question_type == Exercise::COMPLETION #填空题 old_ex_answer = @exercise_question.exercise_standard_answers #当前问题的全部标准答案 - old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).uniq.sort - new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.uniq.sort + old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).sort + new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.sort if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时,才会更新标准答案 new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号 old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_choice_id).uniq #全部的答案数组序号 @@ -280,11 +280,12 @@ class ExerciseQuestionsController < ApplicationController if null_choice_text_count >= ex_answer_pre_count new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array ex_answer_pre_count_array.each do |n| - standard_option = { - :exercise_question_id => @exercise_question.id, - :exercise_choice_id => null_choice_id, - :answer_text => null_choice_text[n-1] + @hash_symbol_null_ = { + :exercise_question_id => @exercise_question.id, + :exercise_choice_id => null_choice_id, + :answer_text => null_choice_text[n - 1] } + standard_option = @hash_symbol_null_ ex_answer_pre[n-1].update(standard_option) end if new_add_choice.count > 0 #表示有新增的 @@ -680,8 +681,9 @@ class ExerciseQuestionsController < ApplicationController normal_status(-1,"已发布/已截止,不允许增删答案!") elsif standard_answer.present? if @exercise_question.question_type == Exercise::COMPLETION - exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq - unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count) + # exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq + # unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count) + unless standard_answer.count == exercise_choice_ids.count normal_status(-1,"已发布/已截止,不允许增删标准答案!") end elsif @exercise_question.question_type == Exercise::SUBJECTIVE diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index a916f5351..791d145c2 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -171,17 +171,18 @@ class FilesController < ApplicationController begin attachment_ids.each do |attachment_id| ori = Attachment.find_by_id(attachment_id) - @course.attachments.each do |att| - @exist = false - if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from - att.created_on = Time.now - att.save - @exist = true - break - end - end - - next if @exist + # 同一个资源可以多次发送到课堂 + # @course.attachments.each do |att| + # @exist = false + # if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from + # att.created_on = Time.now + # att.save + # @exist = true + # break + # end + # end + # + # next if @exist attach_copied_obj = ori.copy attach_copied_obj.container = @course attach_copied_obj.created_on = Time.now diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index 0064914d3..89b6dca27 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -392,59 +392,6 @@ class GamesController < ApplicationController end end - # # 文件更新;数据评测记录 - # # 生成重新评测认证码 - # # content_modified:0 表示文件没有更新;content_modified:1 表示文件有更新 - # def file_update - # path = params[:path].strip unless params[:path].blank? - # myshixun = @game.myshixun - # rev = params[:rev] ? params[:rev] : "master" - # @content_modified = 0 - # # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过 - # # 自动保存的时候evaluate为0;点评测的时候为1 - # if params[:evaluate] == 1 - # record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => myshixun.shixun_id, :game_id => @game.id) - # uid_logger("-- game is #{@game.id}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") - # student_work_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f - # record.update_attributes!(:student_work => student_work_time) - # end - # # 远程版本库文件内容 - # last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"] - # last_content = tran_base64_decode64(last_content) - # - # content = if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present? - # params[:content].gsub(/\t/, ' ') - # else - # params[:content] - # end - # if content != last_content - # @content_modified = 1 - # code_file = @g.edit_file(myshixun.gpid, current_user.login, :content => content, :file_path => path, - # :branch_name => rev, :commit_message => params[:evaluate] == 0 ? "auto commit" : "task commit") - # uid_logger("-- file update #{code_file}") - # # REDO:更新失败的处理 - # raise("文件更新失败") unless code_file - # end - # - # if record.present? - # consume_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f - # record.update_attributes!(:file_update => consume_time) - # end - # - # # status为2说明是重新评测 - # if @game.status == 2 - # code = CODES.sample(8).join - # @resubmit = "#{code}_#{@myshixun.id}" - # end - # - # if content != last_content && code_file.blank? - # raise("实训平台繁忙(繁忙等级:81),请稍后刷新并重试") - # end - # rescue Exception => e - # uid_logger("-- file update failed #{e.message}") - # raise Educoder::TipException.new("#{e.message}") - # end - # 恢复初始代码 # 注意path为当前打开文件的path def reset_original_code diff --git a/app/controllers/graduation_works_controller.rb b/app/controllers/graduation_works_controller.rb index a4a59a3f5..204e0e5d4 100644 --- a/app/controllers/graduation_works_controller.rb +++ b/app/controllers/graduation_works_controller.rb @@ -274,7 +274,7 @@ class GraduationWorksController < ApplicationController @is_author = @work.user_id == current_user.id @work_members = @task.task_type == 1 ? [] : @task.graduation_works.where.not(user_id: @work.user_id). where(group_id: @work.group_id).includes(:user) - @attachments = @work.attachments.where.not(attachtype: 7) + @attachments = @work.attachments.where("attachtype != 7 or attachtype is null") end def comment_list diff --git a/app/controllers/gtopic_banks_controller.rb b/app/controllers/gtopic_banks_controller.rb new file mode 100644 index 000000000..291302bc2 --- /dev/null +++ b/app/controllers/gtopic_banks_controller.rb @@ -0,0 +1,40 @@ +class GtopicBanksController < ApplicationController + before_action :require_login + before_action :find_bank + before_action :bank_admin, only: [:edit, :update] + + def show + @bank_attachments = @bank.attachments + end + + def edit + @attachments = @bank.attachments + end + + def update + ActiveRecord::Base.transaction do + @bank.update_attributes(gtopic_bank_params) + Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids] + normal_status(0, "更新成功") + end + end + + private + + def find_bank + @bank = GtopicBank.find_by!(id: params[:id]) + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? || + (current_user.certification_teacher? && @bank.is_public) + end + + def bank_admin + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? + end + + def gtopic_bank_params + tip_exception("name参数不能为空") if params[:gtopic_bank][:name].blank? + tip_exception("description参数不能为空") if params[:gtopic_bank][:description].blank? + params.require(:gtopic_bank).permit(:name, :topic_type, :topic_source, :topic_property_first, :description, + :topic_property_second, :source_unit, :topic_repeat, :province, :city) + end +end diff --git a/app/controllers/homework_banks_controller.rb b/app/controllers/homework_banks_controller.rb new file mode 100644 index 000000000..7e10509ba --- /dev/null +++ b/app/controllers/homework_banks_controller.rb @@ -0,0 +1,69 @@ +class HomeworkBanksController < ApplicationController + before_action :require_login + before_action :find_bank + before_action :bank_params, only: [:update] + before_action :bank_admin, only: [:update, :destroy, :set_public] + + def show + @bank_attachments = @bank.attachments.where(attachtype: 1) + @reference_attachments = @bank.attachments.where(attachtype: 2) + end + + def update + ActiveRecord::Base.transaction do + if @bank.homework_type == 1 + @bank.update_attributes(name: params[:name], description: params[:description], reference_answer: params[:reference_answer]) + elsif @bank.homework_type == 3 + @bank.update_attributes(name: params[:name], description: params[:description], reference_answer: params[:reference_answer], + base_on_project: params[:base_on_project], min_num: params[:min_num], max_num: params[:max_num]) + end + + # 作业描述的附件 + Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids] + # 作业参考答案的附件 + Attachment.associate_container(params[:reference_attachment_ids], @bank.id, @bank.class, 2) if params[:reference_attachment_ids] + + normal_status(0, "更新成功") + end + end + + def destroy + ActiveRecord::Base.transaction do + @bank.homework_commons.update_all(homework_bank_id: nil) + @bank.destroy! + normal_status("删除成功") + end + end + + def set_public + @bank.update_attributes(is_public: 1) + normal_status("更新成功") + end + + private + + def find_bank + @bank = HomeworkBank.find_by!(id: params[:id]) + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? || + (current_user.certification_teacher? && @bank.is_public) + end + + def bank_admin + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? + end + + def bank_params + tip_exception("name参数不能为空") if params[:homework_bank][:name].blank? + tip_exception("description参数不能为空") if params[:homework_bank][:description].blank? + if @bank.homework_type == 3 + tip_exception("base_on_project参数不能为空") if params[:homework_bank][:base_on_project].nil? + tip_exception("min_num参数不能为空") if params[:homework_bank][:min_num].blank? + tip_exception("max_num参数不能为空") if params[:homework_bank][:max_num].blank? + tip_exception("最小人数不能小于1") if params[:homework_bank][:min_num].to_i < 1 + tip_exception("最大人数不能小于最小人数") if params[:homework_bank][:max_num].to_i < params[:homework_bank][:min_num].to_i + end + params.require(:homework_bank).permit(:name, :description, :reference_answer) if @bank.homework_type == 1 + params.require(:homework_bank).permit(:name, :description, :reference_answer, :min_num, :max_num, :base_on_project) if @bank.homework_type == 3 + end + +end diff --git a/app/controllers/poll_bank_questions_controller.rb b/app/controllers/poll_bank_questions_controller.rb new file mode 100644 index 000000000..71f302115 --- /dev/null +++ b/app/controllers/poll_bank_questions_controller.rb @@ -0,0 +1,158 @@ +class PollBankQuestionsController < ApplicationController + before_action :require_login, :check_auth #用户需登陆 + before_action :get_poll, only:[:create] #获取试卷 + before_action :get_poll_question, except: [:create] #获取试卷的问题及试卷 + before_action :bank_admin #是否为老师 + before_action :validates_params, only: [:create, :update] #传入参数的验证 + + def create + ActiveRecord::Base.transaction do + begin + poll_options = { + :question_title => params[:question_title], + :question_type => params[:question_type], + :is_necessary => params[:is_necessary].to_i, + :question_number => @poll.exercise_bank_questions.count + 1, + :max_choices => params[:max_choices] || nil, + :min_choices => params[:min_choices] || nil + } + @poll_question = @poll.exercise_bank_questions.new(poll_options) + + if params[:insert_id].present? #插入问题时,那么从插入的这个id以后的question_num都将要+1 + insert_poll = @poll.exercise_bank_questions.find_by(id: params[:insert_id]) + if insert_poll.present? #如果该问题存在的话,意思是如果是第一题,那么就不存在插入 + ques_num = insert_poll.question_number.to_i + @poll_question.question_number = ques_num + 1 #更新了问题的位置 + @poll.exercise_bank_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1") + end + end + if @poll_question.save! + if params[:question_type] != 3 + p_answer = params[:question_answers] + p_other_answer = params[:question_other_answer] + # 新增选择题答案选择的选项 + (1..p_answer.count).each do |i| + answer = p_answer[i-1] # 传入的答案的内容 + question_option = { + :choice_position => i, + :choice_text => answer + } + poll_answers = @poll_question.exercise_bank_choices.new question_option + poll_answers.save + end + # 新增答案的其他选项 + if p_other_answer + question_option = { + :choice_position => p_answer.count + 1, + :choice_text => '' + } + poll_answers = @poll_question.exercise_bank_choices.new question_option + poll_answers.save + end + end + end + normal_status("创建成功") + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + raise ActiveRecord::Rollback + end + end + end + + def update + ActiveRecord::Base.transaction do + begin + if @poll_question.question_type < 3 #当为单选题或多选题时 + p_answer = params[:question_answers] + p_other_answer = params[:question_other_answer] + p_answer_count = p_answer.count + @poll_current_answers = @poll_question.exercise_bank_choices.count + @poll_question.exercise_bank_choices.each do |an| + if (p_answer_count < @poll_current_answers) && (p_answer_count..@poll_current_answers).to_a.include?(an.choice_position) + an.destroy + end + end + (1..p_answer_count).each do |i| + answer = @poll_question.exercise_bank_choices.find_choice_custom("choice_position",i).first + if answer # 判断该位置的answer是否存在,存在则更新.不存在则跳到下一步 + answer.choice_text = p_answer[i-1] + answer.choice_position = i + answer.save + else + answer_options = { + :choice_position => i, + :choice_text => p_answer[i-1] + } + @poll_question.exercise_bank_choices.new answer_options + end + end + if p_other_answer #判断答案的其他选项是否存在 + other_answer = @poll_question.exercise_bank_choices.find_choice_custom("choice_text","").first + if other_answer.blank? + question_option = { + :choice_position => p_answer_count + 1, + :choice_text => '' + } + @poll_question.exercise_bank_choices.new question_option + else + other_answer.choice_position = p_answer_count + 1 + other_answer.save + end + end + end + @poll_question.update_attributes(poll_questions_params) + normal_status("问卷更新成功") + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + raise ActiveRecord::Rollback + end + end + end + + private + + def bank_admin + tip_exception(403, "无权限") unless @poll.user_id == current_user.id || current_user.admin? + end + + def get_poll + @poll = ExerciseBank.find_by!(id: params[:exercise_bank_id]) + end + + def get_poll_question + @poll_question = ExerciseBankQuestion.find_by!(id: params[:id]) + @poll = @poll_question.exercise_bank + end + + def poll_questions_params + params.permit(:question_title,:question_type,:is_necessary,:question_number,:max_choices,:min_choices) + end + + def validates_params + tip_exception(-1, "问题标题不能为空!") if params[:question_title].blank? + tip_exception(-1, "是否要求必答的值不能为空!") if params[:is_necessary].blank? + tip_exception(-1, "问题类型不能为空!") if params[:question_type].blank? + if params[:min_choices].present? && params[:max_choices].present? && (params[:min_choices].to_i > params[:max_choices].to_i) + tip_exception(-1, "最小可选不能大于最大可选!") + elsif params[:question_answers].present? && (params[:max_choices].to_i > params[:question_answers].count) + tip_exception(-1, "选择题的最大可选项不能大于答案数!") + elsif [1,3].include?(params[:question_type]) && (params[:max_choices].to_i > 0 || params[:min_choices].to_i > 0) + tip_exception(-1, "单选题或主观题不能有最大或最小选择数!") + elsif params[:question_type] == 3 && (params[:question_answers] || params[:question_other_answer]) + tip_exception(-1, "主观问题不需要可选答案!") + elsif params[:question_type] != 3 + if params[:question_answers].present? && params[:question_answers].include?("") + tip_exception(-1, "选择题不能有空值!") + elsif params[:question_other_answer].present? && params[:question_other_answer].length > 0 + tip_exception(-1, "其他选项不能有值!") + elsif params[:question_type] == 1 && params[:question_answers].count < 2 + tip_exception(-1, "单选题选项不能小于2!") + elsif params[:question_type] == 2 && params[:question_answers].count < 3 + tip_exception(-1, "多选题选项不能小于3!") + end + end + end + +end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 0f301dd4f..bc549cf1d 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -901,9 +901,9 @@ class PollsController < ApplicationController error_question = [] @poll_multi_questions.each do |q| poll_user_votes = current_user.poll_votes.where(poll_question_id:q.id)&.size - if q.max_choices.present? && (poll_user_votes > q.max_choices) + if q.max_choices.present? && (q.max_choices > 0) && (poll_user_votes > q.max_choices) error_messages = "第#{q.question_number}题:超过最大选项限制" - elsif q.min_choices.present? && (poll_user_votes < q.min_choices) + elsif q.min_choices.present? && (q.min_choices > 0)&& (poll_user_votes < q.min_choices) error_messages = "第#{q.question_number}题:不得少于最小选项限制" else error_messages = nil @@ -936,7 +936,7 @@ class PollsController < ApplicationController def commit_result ActiveRecord::Base.transaction do begin - @poll_users = @poll.all_poll_users(current_user.id) + @poll_users = @poll.all_poll_users(current_user.id).where(commit_status:1) # 问卷已提交的用户 @poll_commit_ids = @poll_users.commit_by_status(1).pluck(:user_id) #问卷提交用户的id @page = params[:page] || 1 @limit = params[:limit] || 10 diff --git a/app/controllers/question_banks_controller.rb b/app/controllers/question_banks_controller.rb index 098c07d1b..f09a53dbe 100644 --- a/app/controllers/question_banks_controller.rb +++ b/app/controllers/question_banks_controller.rb @@ -1,6 +1,7 @@ class QuestionBanksController < ApplicationController before_action :require_login, :check_auth - before_action :params_filter + before_action :params_filter, except: [:my_courses] + # before_action :teacher_or_admin, except: [:bank_list] # 题库选用列表 # object_type: # normal 普通作业题库; group 分组作业题库; poll问卷题库; exercise试卷题库; gtask 毕设选题题库;gtopic 毕设任务 @@ -79,10 +80,39 @@ class QuestionBanksController < ApplicationController end end + def my_courses + @courses = current_user.manage_courses.where(is_delete: 0, is_end: 0) + unless params[:search].blank? + @courses = @courses.where("name like ?", "%#{params[:search].strip}%") + end + end + + def send_to_course + banks = object_banks + course = current_user.manage_courses.find_by!(id: params[:course_id]) + banks.each do |bank| + case @object_type + when 'HomeworkBank' # 作业 + quote_homework_bank bank, course + when 'ExerciseBank' + if bank.container_type == 'Exercise' # 试卷 + quote_exercise_bank bank, course + else # 问卷 + quote_poll_bank bank, course + end + when 'GtaskBank' + quote_gtask_bank bank, course + when 'GtopicBank' + quote_gtopic_bank bank, course + end + end + normal_status("发送成功") + end + def destroy bank = current_bank - unless user.admin? || bank.user_id == user.id + unless current_user.admin? || bank.user_id == current_user.id render_forbidden return end @@ -97,11 +127,32 @@ class QuestionBanksController < ApplicationController render_ok end + def multi_delete + @objects = object_banks + @objects.destroy_all + normal_status("删除成功") + end + + def multi_public + @objects = object_banks + @objects.update_all(is_public: true) + normal_status("更新成功") + end + private + def object_banks + banks ||= @object_type.classify.constantize.where(@object_filter).where(id: params[:object_id]) + unless current_user.admin? + banks = banks.where(user_id: current_user.id) + end + banks + end + def current_bank @_current_bank ||= @object_type.classify.constantize.where(@object_filter).find(params[:id]) end + def params_filter type = ["normal", "group", "poll", "exercise", "gtask", "gtopic"] tip_exception("object_type类型不正确") unless type.include?(params[:object_type]) @@ -128,6 +179,10 @@ class QuestionBanksController < ApplicationController end end + def teacher_or_admin + tip_exception(403, "无权限操作") unless current_user.is_certification_teacher || current_user.admin? + end + def quote_homework_bank homework, course ActiveRecord::Base.transaction do # 复制作业的基本信息 diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index d9ab31b8d..8591f9821 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -6,15 +6,18 @@ class ShixunsController < ApplicationController :discusses, :collaborators, :fork_list, :propaedeutics] before_action :check_account, only: [:new, :create, :shixun_exec] + before_action :find_shixun, except: [:index, :new, :create, :menus, :get_recommend_shixuns, + :propaedeutics, :departments, :apply_shixun_mirror, + :get_mirror_script, :download_file] - before_action :find_shixun, :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns, - :propaedeutics, :departments, :apply_shixun_mirror, - :get_mirror_script, :download_file] - before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy] + before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns, + :propaedeutics, :departments, :apply_shixun_mirror, + :get_mirror_script, :download_file] + before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, :add_file] before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :shixun_members_added, :change_manager, :collaborators_delete, - :cancel_publish, :add_collaborators] + :cancel_publish, :add_collaborators, :add_file] before_action :portion_allowed, only: [:copy] before_action :special_allowed, only: [:send_to_course, :search_user_courses] @@ -348,7 +351,8 @@ class ShixunsController < ApplicationController sub_type.each do |mirror| ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) # 实训子镜像服务配置 - ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) + name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置 + ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) if name.present? end end @@ -422,7 +426,12 @@ class ShixunsController < ApplicationController # 超级管理员和运营人员才能保存 中间层服务器pod信息的配置 if current_user.admin? || current_user.business? @shixun.shixun_service_configs.destroy_all - @shixun.shixun_service_configs.create!(service_config_params[:shixun_service_configs]) + service_config_params[:shixun_service_configs].each do |config| + logger.info("####{config[:mirror_repository_id]}") + name = MirrorRepository.find_by_id(config[:mirror_repository_id])&.name + # 不保存没有镜像的配置 + @shixun.shixun_service_configs.create!(config) if name.present? + end end rescue Exception => e uid_logger_error(e.message) diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb index b4677f4e3..519bb7e7d 100644 --- a/app/controllers/student_works_controller.rb +++ b/app/controllers/student_works_controller.rb @@ -92,6 +92,7 @@ class StudentWorksController < ApplicationController student_work.commit_time = Time.now student_work.update_time = Time.now student_work.commit_user_id = current_user.id + student_work.update_user_id = current_user.id student_work.group_id = @homework.homework_type == "group" ? @homework.max_group_id : 0 #提交作品时,计算是否迟交 @@ -112,7 +113,7 @@ class StudentWorksController < ApplicationController homework_common_id: @homework.id, project_id: student_work.project_id, late_penalty: student_work.late_penalty, work_status: student_work.work_status, commit_time: Time.now, update_time: Time.now, group_id: student_work.group_id, - commit_user_id: current_user.id) + commit_user_id: current_user.id, update_user_id: current_user.id) stu_work.save! student_work.attachments.each do |attachment| att = attachment.copy @@ -156,6 +157,7 @@ class StudentWorksController < ApplicationController begin @work.description = params[:description] @work.update_time = Time.now + @work.update_user_id = current_user.id # @work.commit_user_id = current_user.id if @work.save! Attachment.associate_container(params[:attachment_ids], @work.id, @work.class) @@ -172,7 +174,7 @@ class StudentWorksController < ApplicationController # 原成员更新描述、更新时间以及附件 @homework.student_works.where(group_id: @work.group_id, user_id: (work_user_ids & params_user_ids)).each do |work| # work.update_attributes(update_time: Time.now, description: @work.description, commit_user_id: current_user.id) - work.update_attributes(update_time: Time.now, description: @work.description) + work.update_attributes(update_time: Time.now, description: @work.description, update_user_id: current_user.id) work.attachments.destroy_all @work.attachments.each do |attachment| att = attachment.copy @@ -192,7 +194,7 @@ class StudentWorksController < ApplicationController @homework.student_works.where(group_id: @work.group_id, user_id: delete_user_ids). update_all(work_status: 0, description: nil, late_penalty: 0, commit_time: nil, update_time: nil, final_score: nil, teacher_score: nil, student_score: nil, teaching_asistant_score: nil, - work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil) + work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil, update_user_id: nil) # 新增加的成员 (params_user_ids - work_user_ids).each do |user_id| @@ -200,7 +202,7 @@ class StudentWorksController < ApplicationController stu_work.update_attributes(user_id: user_id, description: @work.description, homework_common_id: @homework.id, project_id: @work.project_id, late_penalty: @work.late_penalty, work_status: @work.work_status, commit_time: Time.now, update_time: Time.now, - group_id: @work.group_id, commit_user_id: @work.commit_user_id) + group_id: @work.group_id, commit_user_id: @work.commit_user_id, update_user_id: current_user.id) @work.attachments.each do |attachment| att = attachment.copy att.author_id = attachment.author_id @@ -653,15 +655,15 @@ class StudentWorksController < ApplicationController # 查重作品调分 def adjust_review_score tip_exception("缺少type参数") if params[:type].blank? || !["review", "report"].include?(params[:type]) - if params[:type] == "review" && (params[:score].nil? || params[:challenge_id].nil? || params[:code_rate].nil? || params[:copy_user_id].nil?) + if params[:type] == "review" && (params[:score].blank? || params[:challenge_id].blank? || params[:code_rate].blank? || params[:copy_user_id].blank?) tip_exception("参数错误,score和challenge_id和code_rate和copy_user_id不能为空") - elsif params[:type] == "report" && (params[:score].nil? || params[:challenge_id].nil?) - tip_exception("参数错误,score和challenge_id") + elsif params[:type] == "report" && (params[:score].blank? || params[:challenge_id].blank?) + tip_exception("参数错误,score和challenge_id不能为空") end challenge_setting = @homework.homework_challenge_settings.find_by(challenge_id: params[:challenge_id]) challenge = challenge_setting&.challenge - tip_exception("不能小于零") if params[:score] < 0 - tip_exception("不能大于关卡分值:#{challenge_setting.score}分") if challenge_setting.score < params[:score] + tip_exception("不能小于零") if params[:score].to_i < 0 + tip_exception("不能大于关卡分值:#{challenge_setting.score}分") if challenge_setting.score < params[:score].to_i ActiveRecord::Base.transaction do begin diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index 5b926ee48..d97b7172c 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -270,7 +270,7 @@ class SubjectsController < ApplicationController page = params[:page] || 1 member_ids = @subject.subject_members.map(&:user_id).join(',') condition = "%#{params[:search].strip}%".gsub(" ","") - @users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, firstname, login, mail)) LIKE ?", member_ids, "#{condition}") + @users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, ifnull(firstname, ''), login)) LIKE ?", member_ids, "#{condition}") @users = @users.page(page).per(10) @users = @users.includes(:user_extension) @@ -450,7 +450,7 @@ class SubjectsController < ApplicationController @time = 0 @user_tags = [] else - pass_challenge_ids = pass_games.map(&:challenge_id) + pass_challenge_ids = pass_games.map(&:challenge_id).uniq # 按道理是不用去重的,但是历史数据与重复 subject_challenge_count = @subject.shixuns.sum(:challenges_count) # 用户通关获得的标签 @user_tags = ChallengeTag.where(challenge_id: pass_challenge_ids).pluck(:name) diff --git a/app/controllers/task_banks_controller.rb b/app/controllers/task_banks_controller.rb new file mode 100644 index 000000000..e2e7f0a1a --- /dev/null +++ b/app/controllers/task_banks_controller.rb @@ -0,0 +1,49 @@ +class TaskBanksController < ApplicationController + before_action :require_login + before_action :find_bank + before_action :bank_admin, only: [:update] + + def show + @bank_attachments = @bank.attachments + end + + def update + ActiveRecord::Base.transaction do + begin + @bank.update_attributes(gtask_bank_params) + Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids] + normal_status(0, "更新成功") + rescue Exception => e + uid_logger(e.message) + tip_exception(e.message) + raise ActiveRecord::Rollback + end + end + end + + private + + def find_bank + @bank = GtaskBank.find_by!(id: params[:id]) + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? || + (current_user.certification_teacher? && @bank.is_public) + end + + def bank_admin + tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? + end + + def gtask_bank_params + tip_exception("name参数不能为空") if params[:gtask_bank][:name].blank? + tip_exception("description参数不能为空") if params[:gtask_bank][:description].blank? + if @bank.homework_type == 3 + tip_exception("base_on_project参数不能为空") if params[:gtask_bank][:base_on_project].nil? + tip_exception("min_num参数不能为空") if params[:gtask_bank][:min_num].blank? + tip_exception("max_num参数不能为空") if params[:gtask_bank][:max_num].blank? + tip_exception("最小人数不能小于1") if params[:gtask_bank][:min_num].to_i < 1 + tip_exception("最大人数不能小于最小人数") if params[:gtask_bank][:max_num].to_i < params[:gtask_bank][:min_num].to_i + end + params.require(:gtask_bank).permit(:name, :description) if @bank.task_type == 1 + params.require(:gtask_bank).permit(:name, :description, :min_num, :max_num, :base_on_project) if @bank.task_type == 2 + end +end diff --git a/app/controllers/users/question_banks_controller.rb b/app/controllers/users/question_banks_controller.rb index 0dc4a3f3f..d2f111973 100644 --- a/app/controllers/users/question_banks_controller.rb +++ b/app/controllers/users/question_banks_controller.rb @@ -1,4 +1,5 @@ class Users::QuestionBanksController < Users::BaseController + before_action :require_login before_action :check_query_params! before_action :check_user_permission! @@ -8,7 +9,7 @@ class Users::QuestionBanksController < Users::BaseController @count = question_banks.count @course_lists = service.course_lists - @question_banks = paginate(question_banks.includes(:user, :course_list), special: true) + @question_banks = paginate(question_banks.includes(:user, :course_list)) load_question_banks_solve_count # for solve n + 1 end @@ -18,8 +19,8 @@ class Users::QuestionBanksController < Users::BaseController def load_question_banks_solve_count question_bank_ids = @question_banks.map(&:id) @solve_count_map = - case params[:category] - when 'common', 'group' then + case params[:object_type] + when 'normal', 'group' then StudentWork.where(is_delete: false, work_status: [1, 2, 3]).joins(:homework_common) .where(homework_commons: { homework_bank_id: question_bank_ids }) .group('homework_commons.homework_bank_id').count @@ -36,20 +37,20 @@ class Users::QuestionBanksController < Users::BaseController .group('graduation_tasks.gtask_bank_id').count when 'gtopic' then StudentGraduationTopic.joins(:graduation_topic) - .where(gtopic_banks: { gtopic_bank_id: question_bank_ids }).where('status != 0') - .group('gtopic_banks.gtopic_bank_id').count + .where(graduation_topics: { gtopic_bank_id: question_bank_ids }).where('student_graduation_topics.status = 1') + .group('graduation_topics.gtopic_bank_id').count end end def query_params - params.permit(:type, :category, :course_list_id, :sort_by, :sort_direction) + params.permit(:type, :object_type, :course_list_id, :sort_by, :sort_direction) end def check_query_params! params[:type] = 'personal' if params[:type].blank? || !%w(personal publicly).include?(params[:type]) - if params[:category].blank? || !%w(common group exercise poll gtask gtopic).include?(params[:category]) - params[:category] = 'common' + if params[:object_type].blank? || !%w(normal group exercise poll gtask gtopic).include?(params[:object_type]) + params[:object_type] = 'normal' end if params[:sort_by].blank? || !%w(updated_at name contributor).include?(params[:sort_by]) @@ -62,12 +63,10 @@ class Users::QuestionBanksController < Users::BaseController end def check_user_permission! - return if User.current.admin? || (observed_logged_user? && read_question_bank_permission?) - - render_forbidden - end - - def read_question_bank_permission? - params[:type] == 'personal' ? User.current.is_teacher? : User.current.certification_teacher? + if params[:type] == 'publicly' + render_error("未通过职业认证") unless User.current.admin? || User.current.certification_teacher? + else + render_forbidden unless User.current.admin? || User.current.is_teacher? + end end end \ No newline at end of file diff --git a/app/helpers/admins/mirror_repositories_helper.rb b/app/helpers/admins/mirror_repositories_helper.rb new file mode 100644 index 000000000..d62494e24 --- /dev/null +++ b/app/helpers/admins/mirror_repositories_helper.rb @@ -0,0 +1,23 @@ +module Admins::MirrorRepositoriesHelper + def mirror_type_tag(mirror) + case mirror.main_type + when '1' then ''.html_safe + when '0' then ''.html_safe + end + end + + def mirror_status_tag(mirror) + case mirror.status + when 0 + ''.html_safe + when 1 + ''.html_safe + when 2, 3 + ''.html_safe + when 4 + ''.html_safe + when 5 + ''.html_safe + end + end +end \ No newline at end of file diff --git a/app/helpers/exercise_bank_questions_helper.rb b/app/helpers/exercise_bank_questions_helper.rb new file mode 100644 index 000000000..10df0f5b3 --- /dev/null +++ b/app/helpers/exercise_bank_questions_helper.rb @@ -0,0 +1,2 @@ +module ExerciseBankQuestionsHelper +end diff --git a/app/helpers/homework_banks_helper.rb b/app/helpers/homework_banks_helper.rb new file mode 100644 index 000000000..805479c79 --- /dev/null +++ b/app/helpers/homework_banks_helper.rb @@ -0,0 +1,2 @@ +module HomeworkBanksHelper +end diff --git a/app/helpers/homework_commons_helper.rb b/app/helpers/homework_commons_helper.rb index fa563a72c..a3ed43405 100644 --- a/app/helpers/homework_commons_helper.rb +++ b/app/helpers/homework_commons_helper.rb @@ -48,7 +48,7 @@ module HomeworkCommonsHelper end when 2, 5, 6 status << "评阅中" - time = "评阅剩余时间:" + (course.end_date.present? ? how_much_time(course.end_date.end_of_day) : "") + time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" time_status = 5 end diff --git a/app/helpers/shixuns_helper.rb b/app/helpers/shixuns_helper.rb index d849053ca..b41750bed 100644 --- a/app/helpers/shixuns_helper.rb +++ b/app/helpers/shixuns_helper.rb @@ -14,6 +14,19 @@ module ShixunsHelper %W(未发布 已发布 已关闭)[status-1] end + def shixun_authentication_status shixun + case shixun.try(:status) + when 0,nil + "编辑中" + when 1 + "待审核" + when 2 + "已发布" + when 3 + "已关闭" + end + end + # 已完成实训所获得的经验值 def myshixun_exp myshixun score = 0 diff --git a/app/jobs/delete_department_notify_job.rb b/app/jobs/delete_department_notify_job.rb new file mode 100644 index 000000000..1da5e2e85 --- /dev/null +++ b/app/jobs/delete_department_notify_job.rb @@ -0,0 +1,21 @@ +# 删除部门 消息通知 +class DeleteDepartmentNotifyJob < ApplicationJob + queue_as :notify + + def perform(department_id, operator_id, user_ids) + department = Department.unscoped.find_by(id: department_id) + return if department.blank? || user_ids.blank? + + attrs = %i[ user_id trigger_user_id container_id container_type tiding_type status created_at updated_at] + + same_attrs = { + trigger_user_id: operator_id, container_id: department.id, container_type: 'Department', + status: 4, tiding_type: 'System' + } + Tiding.bulk_insert(*attrs) do |worker| + user_ids.each do |user_id| + worker.add same_attrs.merge(user_id: user_id) + end + end + end +end diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 8087fd6ab..455fd1b6a 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -95,6 +95,14 @@ class Challenge < ApplicationRecord end end + def tags_show + if self.challenge_tags.nil? + "--" + else + self.try(:challenge_tags).map(&:name).join(";") + end + end + ## 选择题答案 def choose_answer result = [] diff --git a/app/models/customer.rb b/app/models/customer.rb new file mode 100644 index 000000000..3baf32738 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,10 @@ +class Customer < ApplicationRecord + default_scope { order(created_at: :desc) } + + belongs_to :school + + has_many :partner_customers, dependent: :destroy + has_many :partners, through: :partner_customers + + has_many :users +end \ No newline at end of file diff --git a/app/models/department.rb b/app/models/department.rb index 9c4a0908b..3b0672cce 100644 --- a/app/models/department.rb +++ b/app/models/department.rb @@ -2,6 +2,18 @@ class Department < ApplicationRecord belongs_to :school has_many :department_members, dependent: :destroy + has_many :member_users, through: :department_members, source: :user + + has_many :user_extensions, dependent: :nullify + has_many :apply_add_departments scope :without_deleted, -> { where(is_delete: false) } + + def member?(user) + department_members.exists?(user_id: user.id) + end + + def soft_delete! + update!(is_delete: true) + end end diff --git a/app/models/exercise_bank.rb b/app/models/exercise_bank.rb index 22c2a5041..067d080b5 100644 --- a/app/models/exercise_bank.rb +++ b/app/models/exercise_bank.rb @@ -18,4 +18,7 @@ class ExerciseBank < ApplicationRecord scope :exercise_bank_search, lambda { |keywords| where("name LIKE ?", "%#{keywords}%") unless keywords.blank?} + validates :name, length: { maximum: 60, too_long: "60 characters is the maximum allowed" } + validates :description, length: { maximum: 100, too_long: "100 characters is the maximum allowed" } + end \ No newline at end of file diff --git a/app/models/exercise_bank_choice.rb b/app/models/exercise_bank_choice.rb index d3a91bb02..be29ca786 100644 --- a/app/models/exercise_bank_choice.rb +++ b/app/models/exercise_bank_choice.rb @@ -1,4 +1,7 @@ class ExerciseBankChoice < ApplicationRecord belongs_to :exercise_bank_question has_many :exercise_bank_standard_answers + + scope :find_choice_custom, lambda {|k,v| where("#{k} = ?",v)} #根据传入的参数查找问题 + scope :left_choice_choose, lambda {|k,v| where("#{k} > ?",v)} #根据传入的参数查找问题 end \ No newline at end of file diff --git a/app/models/exercise_bank_question.rb b/app/models/exercise_bank_question.rb index 5a39fd5d2..fdd343f5a 100644 --- a/app/models/exercise_bank_question.rb +++ b/app/models/exercise_bank_question.rb @@ -1,17 +1,24 @@ class ExerciseBankQuestion < ApplicationRecord belongs_to :exercise_bank - belongs_to :shixun + belongs_to :shixun, optional: true has_many :exercise_bank_shixun_challenges,:dependent => :destroy has_many :exercise_bank_choices, :dependent => :destroy has_many :exercise_bank_standard_answers, :dependent => :destroy #attr_accessible :question_number, :question_score, :question_title, :question_type + scope :insert_question_ex, lambda {|k| where("question_number > ?",k)} + scope :find_by_custom, lambda {|k,v| where("#{k} = ?",v)} #根据传入的参数查找问题 + scope :left_question_choose, lambda {|k,v| where("#{k} > ?",v)} #根据传入的参数查找问题 + scope :find_objective_questions, -> {where("question_type != ?",4)} #查找全部客观题 + def question_type_name case self.question_type - when 1 + when 0 "单选题" - when 2 + when 1 "多选题" + when 2 + "判断题" when 3 "填空题" when 4 @@ -20,4 +27,14 @@ class ExerciseBankQuestion < ApplicationRecord "实训题" end end + + + #获取问题的全部标准答案 + def get_standard_answer_ids + exercise_bank_standard_answers.pluck(:exercise_bank_choice_id) + end + + def get_standard_answer_text + exercise_bank_standard_answers.pluck(:answer_text) + end end \ No newline at end of file diff --git a/app/models/exercise_bank_standard_answer.rb b/app/models/exercise_bank_standard_answer.rb index 9cd82afe6..2535473b0 100644 --- a/app/models/exercise_bank_standard_answer.rb +++ b/app/models/exercise_bank_standard_answer.rb @@ -2,4 +2,5 @@ class ExerciseBankStandardAnswer < ApplicationRecord belongs_to :exercise_bank_question belongs_to :exercise_bank_choice #attr_accessible :answer_text + scope :standard_by_ids, lambda { |s| where(exercise_bank_choice_id: s) } end \ No newline at end of file diff --git a/app/models/homework_common.rb b/app/models/homework_common.rb index ac11a54b7..c33bbca0d 100644 --- a/app/models/homework_common.rb +++ b/app/models/homework_common.rb @@ -3,7 +3,7 @@ class HomeworkCommon < ApplicationRecord enum homework_type: { normal: 1, program: 2, group: 3, practice: 4 }, _suffix: true has_many :homework_group_settings, dependent: :destroy has_many :published_settings, -> { group_published }, class_name: "HomeworkGroupSetting" - has_many :student_works, -> { where("is_delete = 0") } + has_many :student_works, -> { where(is_delete: 0) } has_many :score_student_works, -> { where("is_delete = 0 and work_status != 0").order("work_score desc") }, class_name: "StudentWork" has_one :homework_detail_manual, dependent: :destroy diff --git a/app/models/library_apply.rb b/app/models/library_apply.rb index 0a7ec8aec..85944169b 100644 --- a/app/models/library_apply.rb +++ b/app/models/library_apply.rb @@ -16,4 +16,8 @@ class LibraryApply < ApplicationRecord transitions from: :pending, to: :agreed end end + + def status_i18n + + end end \ No newline at end of file diff --git a/app/models/mirror_operation_record.rb b/app/models/mirror_operation_record.rb new file mode 100644 index 000000000..39e352a0f --- /dev/null +++ b/app/models/mirror_operation_record.rb @@ -0,0 +1,7 @@ +# status: 0 创建镜像; 1 修改镜像ID; 2 修改镜像name 3 删除镜像 4.从主节点同步镜像到子节点(子节点发生异常), 5. 修改镜像别名, 6. 修改镜像的状态 +# user_id: -1时,证明是非人为因素造成,中间层异常导致 +class MirrorOperationRecord < ActiveRecord::Base + default_scope { order(created_at: :desc) } + + belongs_to :mirror_repository +end diff --git a/app/models/mirror_repository.rb b/app/models/mirror_repository.rb index be26b5ad1..e29b008ad 100644 --- a/app/models/mirror_repository.rb +++ b/app/models/mirror_repository.rb @@ -8,4 +8,8 @@ class MirrorRepository < ApplicationRecord scope :published_mirror, -> { where(status: 1) } scope :published_main_mirror, -> { published_mirror.where(main_type: 1) } scope :published_small_mirror, -> { published_mirror.where(main_type: 0) } + + def deletable? + status != 1 && !shixun_mirror_repositories.exists? + end end diff --git a/app/models/myshixun.rb b/app/models/myshixun.rb index 80074df6e..006bbf26d 100644 --- a/app/models/myshixun.rb +++ b/app/models/myshixun.rb @@ -7,6 +7,9 @@ class Myshixun < ApplicationRecord belongs_to :user belongs_to :shixun, counter_cache: true + has_one :last_executable_task, -> { where(status: [0, 1]).reorder(created_at: :asc) }, class_name: 'Game' + has_one :last_task, -> { all }, class_name: 'Game' + validates_uniqueness_of :shixun_id, :scope => :user_id, :message => "shixun_id and user_id unique error" scope :finished, lambda { where(status: 1) } scope :search_myshixun_user, ->(user_id){where(user_id:user_id)} diff --git a/app/models/partner.rb b/app/models/partner.rb index f2f8cca2a..9bff82028 100644 --- a/app/models/partner.rb +++ b/app/models/partner.rb @@ -1,3 +1,5 @@ class Partner < ApplicationRecord + belongs_to :school, optional: true + has_many :users end diff --git a/app/models/partner_customer.rb b/app/models/partner_customer.rb new file mode 100644 index 000000000..b3f4e567b --- /dev/null +++ b/app/models/partner_customer.rb @@ -0,0 +1,4 @@ +class PartnerCustomer < ApplicationRecord + belongs_to :partner + belongs_to :customer +end \ No newline at end of file diff --git a/app/models/school.rb b/app/models/school.rb index 8a28ae4bf..1034fd997 100644 --- a/app/models/school.rb +++ b/app/models/school.rb @@ -13,6 +13,9 @@ class School < ApplicationRecord has_many :school_daily_reports, dependent: :destroy has_many :courses + has_many :customers, dependent: :destroy + has_many :partners, dependent: :destroy + # 学校管理员 def manager?(user) ec_school_users.exists?(user_id: user.id) diff --git a/app/models/shixun.rb b/app/models/shixun.rb index d61ba45b9..db6bca43e 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -78,6 +78,29 @@ class Shixun < ApplicationRecord shixun_info.try(:evaluate_script) end + def fork_identifier + self.fork_from.nil? ? "--" : Shixun.where(id: self.fork_from).first.try(:identifier) + end + + def shixun_status + status = "" + case self.status + when 0 + status = "编辑中" + when 1 + status = "审核中" + when 2 + status = "已发布" + when 3 + status = "已关闭" + end + status + end + + def is_tag_used?(id) + tag_repertoires.map(&:id).include?(id) + end + # 实训用户tag def user_tags_name(user = User.current) Shixun.joins(challenges: [:challenge_tags, :games]).where(games: {status: 2, user_id: user.id}, shixuns: {id:id}) @@ -133,6 +156,10 @@ class Shixun < ApplicationRecord User.find(self.user_id) end + def shixun_main_name + self.mirror_repositories.published_main_mirror.first.try(:type_name) + end + def is_published? status > 1 end diff --git a/app/models/student_work.rb b/app/models/student_work.rb index 074068273..168cfeb68 100644 --- a/app/models/student_work.rb +++ b/app/models/student_work.rb @@ -2,6 +2,7 @@ class StudentWork < ApplicationRecord #学生提交作品表 #work_status :0 未提交 1 已提交 2 迟交 belongs_to :user belongs_to :commit_user, class_name: 'User', foreign_key: :commit_user_id, optional: true + belongs_to :update_user, class_name: 'User', foreign_key: :update_user_id, optional: true belongs_to :homework_common belongs_to :myshixun, optional: true has_many :student_works_evaluation_distributions, dependent: :destroy diff --git a/app/models/user.rb b/app/models/user.rb index 5ba8667f5..12759ea4d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -139,7 +139,7 @@ class User < ApplicationRecord has_many :videos, dependent: :destroy # 客户管理 - belongs_to :partner + belongs_to :partner, optional: true # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } diff --git a/app/queries/admins/department_query.rb b/app/queries/admins/department_query.rb new file mode 100644 index 000000000..f0b8c5d24 --- /dev/null +++ b/app/queries/admins/department_query.rb @@ -0,0 +1,32 @@ +class Admins::DepartmentQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + departments = Department.where(is_auth: true).without_deleted + + keyword = params[:keyword].to_s.strip + if keyword.present? + departments = departments.joins(:school) + .where('schools.name LIKE :keyword OR departments.name LIKE :keyword', keyword: "%#{keyword}%") + end + + if params[:with_member].to_s == 'true' + subquery = DepartmentMember.where('department_id = departments.id').select('1 AS one').to_sql + departments = departments.where("EXISTS(#{subquery})") + end + + if params[:with_identifier].to_s == 'true' + departments = departments.where.not(identifier: nil).where.not(identifier: '') + end + + custom_sort(departments, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/library_apply_query.rb b/app/queries/admins/library_apply_query.rb new file mode 100644 index 000000000..9fdc2d067 --- /dev/null +++ b/app/queries/admins/library_apply_query.rb @@ -0,0 +1,29 @@ +class Admins::LibraryApplyQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :updated_at, default_by: :updated_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + status = + case params[:status] + when 'processed' then %w(agreed refused) + else params[:status] + end + applies = LibraryApply.where(status: status) if status.present? + + # 关键字模糊查询 + keyword = params[:keyword].to_s.strip + if keyword.present? + applies = applies.joins(:library) + .where('title LIKE :keyword OR uuid LIKE :keyword', keyword: "%#{keyword}%") + end + + custom_sort(applies, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/myshixun_query.rb b/app/queries/admins/myshixun_query.rb new file mode 100644 index 000000000..bfbb98d64 --- /dev/null +++ b/app/queries/admins/myshixun_query.rb @@ -0,0 +1,26 @@ +class Admins::MyshixunQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + objs = Myshixun.all + + keyword = params[:keyword].to_s.strip + if keyword.present? + like_sql = 'users.login LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR '\ + 'schools.name LIKE :keyword OR shixuns.name LIKE :keyword OR CONCAT(teacher.lastname, teacher.firstname) Like :keyword' + objs = objs.joins(:shixun, user: { user_extension: :school }) + .joins('JOIN users teacher ON teacher.id = shixuns.user_id') + .where(like_sql, keyword: "%#{keyword}%") + end + + custom_sort(objs, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/shixun_query.rb b/app/queries/admins/shixun_query.rb new file mode 100644 index 000000000..0d726f267 --- /dev/null +++ b/app/queries/admins/shixun_query.rb @@ -0,0 +1,48 @@ +class Admins::ShixunQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + all_shixuns = Shixun.all + status = + case params[:status] + when "editing" then [0] + when "pending" then [1] + when "processed" then [2] + when "closed" then [3] + else + [0,1,2,3] + end + + all_shixuns = all_shixuns.where(status: status) if status.present? + + if params[:tag].present? + all_shixuns = all_shixuns.joins(:mirror_repositories).where("mirror_repositories.id = ?",params[:tag].to_i) + end + + # 关键字模糊查询 + keyword = params[:keyword].to_s.strip + if keyword.present? + search_type = params[:search_type] || "0" + case search_type + when "0" + all_shixuns = all_shixuns.joins(:user) + .where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%") + when "1" + all_shixuns = all_shixuns.where('name like :keyword', keyword: "%#{keyword}%") + else + all_shixuns = all_shixuns.joins(user: {user_extension: :school}).where('schools.name LIKE ?', "%#{keyword}%") + end + + end + + custom_sort(all_shixuns, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/shixun_settings_query.rb b/app/queries/admins/shixun_settings_query.rb new file mode 100644 index 000000000..32cfdb8bf --- /dev/null +++ b/app/queries/admins/shixun_settings_query.rb @@ -0,0 +1,54 @@ +class Admins::ShixunSettingsQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + all_shixuns = Shixun.all + status = + case params[:status] + when "editing" then [0] + when "pending" then [1] + when "processed" then [2] + when "closed" then [3] + else + [0,1,2,3] + end + + all_shixuns = all_shixuns.where(status: status) if status.present? + + if params[:tag].present? + all_shixuns = all_shixuns.joins(:mirror_repositories).where("mirror_repositories.id = ?",params[:tag].to_i) + end + + # 关键字模糊查询 + keyword = params[:keyword].to_s.strip + if keyword.present? + search_type = params[:search_type] || "0" + case search_type + when "0" + all_shixuns = all_shixuns.joins(:user) + .where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%") + when "1" + all_shixuns = all_shixuns.where('name like :keyword', keyword: "%#{keyword}%") + else + all_shixuns = all_shixuns.joins(user: {user_extension: :school}).where('schools.name LIKE ?', "%#{keyword}%") + end + end + + all_shixuns = all_shixuns.where(can_copy: params[:can_copy]) if params[:can_copy] + all_shixuns = all_shixuns.where(webssh: params[:webssh]) if params[:webssh] + all_shixuns = all_shixuns.where(hidden: params[:hidden]) if params[:hidden] + all_shixuns = all_shixuns.where(homepage_show: params[:homepage_show]) if params[:homepage_show] + all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass] + all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden] + + custom_sort(all_shixuns, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/user_query.rb b/app/queries/admins/user_query.rb index 5a633f059..75e50fc1b 100644 --- a/app/queries/admins/user_query.rb +++ b/app/queries/admins/user_query.rb @@ -28,7 +28,13 @@ class Admins::UserQuery < ApplicationQuery 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) + users = users.where(sql, keyword: "%#{keyword}%") + end + + # 姓名 + name = params[:name].to_s.strip.presence + if name.present? + users = users.where('CONCAT(lastname, firstname) LIKE :name', name: "%#{name}%") end # 学校名称 diff --git a/app/services/admins/add_department_member_service.rb b/app/services/admins/add_department_member_service.rb new file mode 100644 index 000000000..f8331cf4a --- /dev/null +++ b/app/services/admins/add_department_member_service.rb @@ -0,0 +1,20 @@ +class Admins::AddDepartmentMemberService < ApplicationService + + attr_reader :department, :params + + def initialize(department, params) + @department = department + @params = params + end + + def call + columns = %i[] + DepartmentMember.bulk_insert(*columns) do |worker| + Array.wrap(params[:user_ids]).compact.each do |user_id| + next if department.department_members.exists?(user_id: user_id) + + worker.add(department_id: department.id, user_id: user_id) + end + end + end +end \ No newline at end of file diff --git a/app/services/admins/check_shixun_mirrors_service.rb b/app/services/admins/check_shixun_mirrors_service.rb new file mode 100644 index 000000000..868fab042 --- /dev/null +++ b/app/services/admins/check_shixun_mirrors_service.rb @@ -0,0 +1,89 @@ +class Admins::CheckShixunMirrorsService < ApplicationService + Error = Class.new(StandardError) + + def call + bridge_images + + ActiveRecord::Base.transaction do + check_sync_mirrors! + + check_mirrors! + end + end + + private + + def mirrors + bridge_images['images'] + end + + def sync_mirrors + bridge_images['imagesNotSync'] + end + + def check_mirrors! + return if mirrors.blank? + image_names = [] + + mirrors.each do |data| + mirror = JSON.parse(data) + + name_repository = MirrorRepository.find_by(name: mirror['imageName']) + id_repository = MirrorRepository.find_by(mirrorID: mirror['imageID']) + + image_names << mirror['imageName'] + + if name_repository.blank? && id_repository.present? # 镜像名称被修改 + id_repository.update_column(:status, 2) + MirrorOperationRecord.create!(mirror_repository_id: id_repository.id, mirror_id: mirror['imageID'], + mirror_name: mirror['imageName'], status: 2, user_id: -1) + elsif name_repository.blank? # 镜像不存在、创建镜像 + new_repository = MirrorRepository.create!(mirrorID: mirror['imageID'], name: mirror['imageName']) + MirrorOperationRecord.create!(mirror_repository_id: new_repository.id, mirror_id: mirror['imageID'], + mirror_name: mirror['imageName'], status: 0, user_id: -1) + elsif name_repository.mirrorID != mirror['imageID'] # 镜像ID被修改 + name_repository.update_column(:status, 2) + MirrorOperationRecord.create!(mirror_repository_id: name_repository.id, mirror_id: mirror['imageID'], + mirror_name: mirror['imageName'], status: 1, user_id: -1) + end + end + + # 判断中间层镜像是否被删除 + MirrorRepository.find_each do |mirror| + next if mirror&.name.blank? || image_names.index(mirror.name) + + mirror.update_column(:status, 4) + MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: mirror&.mirrorID, + mirror_name: mirror.name, status: 3, user_id: -1) + end + end + + def check_sync_mirrors! + return if sync_mirrors.blank? + + sync_mirrors.each do |data| + mirror = JSON.parse(data) + + repository = MirrorRepository.find_by(name: mirror['imageName']) + next if repository.blank? || repository.status != 1 + + repository.update_column(:status, 5) + MirrorOperationRecord.create!(mirror_repository_id: repository.id, mirror_id: mirror['imageID'], + mirror_name: mirror['imageName'], status: 4, user_id: -1) + end + end + + def bridge_images + @_bridge_images ||= begin + url = EduSetting.get('cloud_bridge') + res = Faraday.get(url) + + raise Error, '拉取镜像信息异常' if res && res['code'].nonzero? + + res + rescue => e + Rails.logger.error("get response failed ! #{e.message}") + raise Error, '实训云平台繁忙(繁忙等级:84)' + end + end +end \ No newline at end of file diff --git a/app/services/admins/choose_mirror_service.rb b/app/services/admins/choose_mirror_service.rb new file mode 100644 index 000000000..77d187884 --- /dev/null +++ b/app/services/admins/choose_mirror_service.rb @@ -0,0 +1,21 @@ +class Admins::ChooseMirrorService < ApplicationService + attr_reader :mirror, :user, :number + + def initialize(mirror, user, mirror_number) + @mirror = mirror + @user = user + @number = mirror_number + end + + def call + if mirror.mirrorID == number + mirror.update_column(:status, 1) + return + end + + old_number = mirror.mirrorID + mirror.update!(mirrorID: number, status: 1) + MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: number, mirror_name: mirror.name, + status: 1, user_id: user.id, old_tag: old_number, new_tag: mirror.mirrorID) + end +end \ No newline at end of file diff --git a/app/services/admins/save_mirror_repository_service.rb b/app/services/admins/save_mirror_repository_service.rb new file mode 100644 index 000000000..4aff64f66 --- /dev/null +++ b/app/services/admins/save_mirror_repository_service.rb @@ -0,0 +1,37 @@ +class Admins::SaveMirrorRepositoryService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :mirror, :user, :params + + def initialize(mirror, user, params) + @mirror = mirror + @user = user + @params = params + end + + def call + mirror.assign_attributes(params) + + raise Error, '镜像别名重复' if MirrorRepository.where.not(id: mirror.id).exists?(type_name: params[:type_name]) + + ActiveRecord::Base.transaction do + record_operation! if mirror.persisted? + + mirror.save! + end + end + + private + + def record_operation! + if mirror.type_name_changed? + MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5, + user_id: user.id, old_tag: mirror.type_name_in_database, + new_tag: mirror.type_name) + elsif mirror.status_changed? + MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5, + user_id: user.id, old_tag: mirror.status_in_database, + new_tag: mirror.status) + end + end +end \ No newline at end of file diff --git a/app/services/homeworks_service.rb b/app/services/homeworks_service.rb index 64aec92c6..f6868afba 100644 --- a/app/services/homeworks_service.rb +++ b/app/services/homeworks_service.rb @@ -298,6 +298,9 @@ class HomeworksService else 0 end + else + adjust_score = work.challenge_work_scores.select{|work_score| work_score.challenge_id == game.challenge_id}.last + final_score += adjust_score.score if adjust_score.present? end end @@ -314,8 +317,8 @@ class HomeworksService if work.work_status != 0 if myshixun_endtime.present? - work.cost_time = myshixun_endtime.to_i - setting_time.publish_time.to_i - + work_cost_time = myshixun_endtime.to_i - setting_time.publish_time.to_i + work.cost_time = work_cost_time > 0 ? work_cost_time : games.select{|game| game.status == 2}.pluck(:cost_time).sum efficiency = (pass_consume_time == 0 ? 0 : Math.log((user_total_score / pass_consume_time.to_f) + 1.0)) work.efficiency = format("%.2f", efficiency) diff --git a/app/services/users/question_bank_service.rb b/app/services/users/question_bank_service.rb index b17073a4f..7e640c6a5 100644 --- a/app/services/users/question_bank_service.rb +++ b/app/services/users/question_bank_service.rb @@ -22,8 +22,8 @@ class Users::QuestionBankService course_lists = CourseList.joins(relation_name).where.not(relation_name => { id: nil }) category_condition = - case params[:category] - when 'common' then { homework_type: 1 } + case params[:object_type] + when 'normal' then { homework_type: 1 } when 'group' then { homework_type: 3 } when 'exercise' then { container_type: 'Exercise' } when 'poll' then { container_type: 'Poll' } @@ -47,8 +47,8 @@ class Users::QuestionBankService def class_name @_class_name ||= begin - case params[:category] - when 'common', 'group' then 'HomeworkBank' + case params[:object_type] + when 'normal', 'group' then 'HomeworkBank' when 'exercise', 'poll' then 'ExerciseBank' when 'gtask' then 'GtaskBank' when 'gtopic' then 'GtopicBank' @@ -58,8 +58,8 @@ class Users::QuestionBankService end def category_filter(relations) - case params[:category] - when 'common' then + case params[:object_type] + when 'normal' then relations.where(homework_type: 1) when 'group' then relations.where(homework_type: 3) diff --git a/app/templates/shared/main.css b/app/templates/shared/main.css index 9e9bc4f50..884f6fa37 100644 --- a/app/templates/shared/main.css +++ b/app/templates/shared/main.css @@ -344,7 +344,7 @@ a.edu-txt-w40,.edu-txt-w40{ width:40px; display: inline-block;text-align: center .bor-grey01{border:1px solid #E6EAEB;} .bor-orange{border:1px solid #FF7500;} .bor-blue{border:1px solid #5faee3;} -.bor-red{border:1px solid #db0505;} +.bor-red{border:1px solid #db0505 !important;} .bor-none{border:none;} .bor-outnone{outline:none; border:0px;} diff --git a/app/templates/shixun_work/shixun_work.html.erb b/app/templates/shixun_work/shixun_work.html.erb index f4aec2ea6..a6ef48c3e 100644 --- a/app/templates/shixun_work/shixun_work.html.erb +++ b/app/templates/shixun_work/shixun_work.html.erb @@ -66,15 +66,19 @@ - - + + + + <% @games.each_with_index do |game, index| %> + <% challenge_score = @homework.challenge_score game.challenge_id %> + <% game_score = @work.work_challenge_score game, challenge_score %> + + <% end %> diff --git a/app/views/admins/choose_mirror_repositories/create.js.erb b/app/views/admins/choose_mirror_repositories/create.js.erb new file mode 100644 index 000000000..585ecb1af --- /dev/null +++ b/app/views/admins/choose_mirror_repositories/create.js.erb @@ -0,0 +1,5 @@ +$.notify({ message: '操作成功' },{ type: 'success' }); + +setTimeout(function(){ + window.location.reload(); +}, 500) \ No newline at end of file diff --git a/app/views/admins/choose_mirror_repositories/new.js.erb b/app/views/admins/choose_mirror_repositories/new.js.erb new file mode 100644 index 000000000..8603011ab --- /dev/null +++ b/app/views/admins/choose_mirror_repositories/new.js.erb @@ -0,0 +1,2 @@ +$('.admin-modal-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/choose_mirror_modal', locals: { mirror: @mirror, new_mirror: @new_mirror } ) %>"); +$('.modal.admin-choose-mirror-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/daily_school_statistics/index.html.erb b/app/views/admins/daily_school_statistics/index.html.erb index 054e06fc6..39dcba633 100644 --- a/app/views/admins/daily_school_statistics/index.html.erb +++ b/app/views/admins/daily_school_statistics/index.html.erb @@ -5,7 +5,7 @@
<%= 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') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> <%#= link_to '导出Excel', export_admins_daily_school_statistics_path(format: :xlsx), class: 'btn btn-outline-primary export-action' %> diff --git a/app/views/admins/dashboards/index.html.erb b/app/views/admins/dashboards/index.html.erb index bff34dbcd..e02c307a5 100644 --- a/app/views/admins/dashboards/index.html.erb +++ b/app/views/admins/dashboards/index.html.erb @@ -12,19 +12,19 @@
-
Traffic
- 350,897 +
当日活跃用户
+ <%= @active_user_count %>
-
- +
+
-

- 3.48% - Since last month -

+ + + +
@@ -33,19 +33,19 @@
-
New users
- 2,356 +
7天内活跃用户数
+ <%= @weekly_active_user_count %>
-
- +
+
-

- 3.48% - Since last week -

+ + + +
@@ -54,19 +54,19 @@
-
Sales
- 924 +
30天内活跃用户数
+ <%= @month_active_user_count %>
-
- +
+
-

- 1.10% - Since yesterday -

+ + + +
@@ -75,19 +75,19 @@
-
Performance
- 49,65% +
30天内新增用户数
+ <%= @new_user_count %>
-
- +
+
-

- 12% - Since last month -

+ + + +
@@ -95,94 +95,124 @@ -
+ +
-
-
-
-

Page visits

-
-
- Test -
-
-
-
- -
关卡任务名称开启时间任务名称开启时间 评测次数 完成时间 耗时 经验值关卡得分调分
<%= index + 1 %> @@ -88,6 +92,8 @@ <%= finished_time game.end_time %> <%= ApplicationController.helpers.time_consuming game %> <%= game.final_score %> / <%= game.challenge.all_score %><%= game_score %> / <%= challenge_score %><%= game_score %>
- - - - - - - - - - <% 5.times do %> - - - - - - - <% end %> - -
TestTestTestTest
/test/4,569340 - 46,53% -
- + + + + + + + + +
-
-

Test

-
-
- Test -
+
30天内新增用户
- - - - - - - - - - - <% 5.times do %> - - - - - - <% end %> - -
TestTest
- Test - - 1,480 - -
- 60% -
-
-
-
-
-
-
+
- \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%# 5.times do %> + + + + + + + + + <%# end %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%# 5.times do %> + + + + + + + + + + + + + + + + + + + <%# end %> + + + + + + + \ No newline at end of file diff --git a/app/views/admins/department_members/create.js.erb b/app/views/admins/department_members/create.js.erb new file mode 100644 index 000000000..4355c7432 --- /dev/null +++ b/app/views/admins/department_members/create.js.erb @@ -0,0 +1,4 @@ +$('.modal.admin-add-department-member-modal').modal('hide'); +$.notify({ message: '操作成功' }); + +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file diff --git a/app/views/admins/department_members/destroy.js.erb b/app/views/admins/department_members/destroy.js.erb new file mode 100644 index 000000000..d3eb3755b --- /dev/null +++ b/app/views/admins/department_members/destroy.js.erb @@ -0,0 +1,2 @@ +$.notify({ message: '操作成功' }); +$('.department-list-container .department-item-<%= current_department.id %> .member-user-item-<%= @member.user_id %>').remove(); \ No newline at end of file diff --git a/app/views/admins/departments/edit.js.erb b/app/views/admins/departments/edit.js.erb new file mode 100644 index 000000000..dc86d3ae0 --- /dev/null +++ b/app/views/admins/departments/edit.js.erb @@ -0,0 +1,2 @@ +$('.admin-modal-container').html("<%= j( render partial: 'admins/departments/shared/edit_department_modal', locals: { department: current_department } ) %>"); +$('.modal.admin-edit-department-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/departments/index.html.erb b/app/views/admins/departments/index.html.erb new file mode 100644 index 000000000..9d0d49358 --- /dev/null +++ b/app/views/admins/departments/index.html.erb @@ -0,0 +1,33 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('部门列表') %> +<% end %> + +
+ <%= form_tag(admins_departments_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
+ <%= hidden_field_tag(:with_member, false,id:'') %> + <%= check_box_tag(:with_member, true, params[:with_member].to_s == 'true', class: 'form-check-input') %> + +
+ +
+ <%= hidden_field_tag(:with_identifier, false,id:'') %> + <%= check_box_tag(:with_identifier, true, params[:with_identifier].to_s == 'true', class: 'form-check-input') %> + +
+ + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '部门/单位名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + + <%= javascript_void_link '新建部门', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-department-modal' } %> +
+ +
+ <%= render partial: 'admins/departments/shared/list', + locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count } %> +
+ +<%= render 'admins/departments/shared/create_department_modal' %> +<%= render 'admins/departments/shared/add_department_member_modal' %> +<%= render 'admins/departments/shared/merge_department_modal' %> \ No newline at end of file diff --git a/app/views/admins/departments/index.js.erb b/app/views/admins/departments/index.js.erb new file mode 100644 index 000000000..bd2e4b25d --- /dev/null +++ b/app/views/admins/departments/index.js.erb @@ -0,0 +1 @@ +$('.department-list-container').html("<%= j(render partial: 'admins/departments/shared/list', locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count }) %>"); \ No newline at end of file diff --git a/app/views/admins/departments/shared/_add_department_member_modal.html.erb b/app/views/admins/departments/shared/_add_department_member_modal.html.erb new file mode 100644 index 000000000..5d2707222 --- /dev/null +++ b/app/views/admins/departments/shared/_add_department_member_modal.html.erb @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_create_department_modal.html.erb b/app/views/admins/departments/shared/_create_department_modal.html.erb new file mode 100644 index 000000000..ae6605eb8 --- /dev/null +++ b/app/views/admins/departments/shared/_create_department_modal.html.erb @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_department_item.html.erb b/app/views/admins/departments/shared/_department_item.html.erb new file mode 100644 index 000000000..11584909d --- /dev/null +++ b/app/views/admins/departments/shared/_department_item.html.erb @@ -0,0 +1,34 @@ +<% not_list = defined?(:users_count) %> + +<%= overflow_hidden_span department.name, width: 150 %> +<%= overflow_hidden_span department.school.name, width: 150 %> + +<% if not_list %> + <%= department.user_extensions.count %> + <%= department.user_extensions.joins(:user).where(users: { professional_certification: true }).count %> +<% else %> + <%= users_count.fetch(department.id, 0) %> + <%= professional_auth_count.fetch(department.id, 0) %> +<% end %> + + + <%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %> + + + <% if department.identifier.present? %> + <%= link_to department.identifier.to_s, statistics_college_path(department.identifier), target: '_blank' %> + <% else %> + -- + <% end %> + +<%= department.host_count %> +<%= department.created_at&.strftime('%Y-%m-%d %H:%M') %> + + <%= link_to '编辑', edit_admins_department_path(department), remote: true, class: 'action' %> + + <%= javascript_void_link '添加管理员', class: 'action', data: { department_id: department.id, toggle: 'modal', target: '.admin-add-department-member-modal' } %> + + <%= javascript_void_link '更改', class: 'action', data: { school_id: department.school_id, department_id: department.id, toggle: 'modal', target: '.admin-merge-department-modal' } %> + + <%= delete_link '删除', admins_department_path(department, element: ".department-item-#{department.id}"), class: 'delete-department-action' %> + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_edit_department_modal.html.erb b/app/views/admins/departments/shared/_edit_department_modal.html.erb new file mode 100644 index 000000000..38b43bbce --- /dev/null +++ b/app/views/admins/departments/shared/_edit_department_modal.html.erb @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_list.html.erb b/app/views/admins/departments/shared/_list.html.erb new file mode 100644 index 000000000..6af63d6f4 --- /dev/null +++ b/app/views/admins/departments/shared/_list.html.erb @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + <% if departments.present? %> + <% departments.each do |department| %> + + <%= render 'admins/departments/shared/department_item', department: department %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
部门名称单位名称用户数已职业认证部门管理员统计链接云主机数<%= sort_tag('创建时间', name: 'created_at', path: admins_departments_path) %>操作
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: departments } %> \ No newline at end of file diff --git a/app/views/admins/departments/shared/_member_users.html.erb b/app/views/admins/departments/shared/_member_users.html.erb new file mode 100644 index 000000000..8d4d466db --- /dev/null +++ b/app/views/admins/departments/shared/_member_users.html.erb @@ -0,0 +1,12 @@ +
+ <% department.member_users.each do |user| %> + + <%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %> + <%= link_to(admins_department_department_member_path(department, user_id: user.id), + method: :delete, remote: true, class: 'ml-1 delete-member-action', + data: { confirm: '确认删除吗?' }) do %> + + <% end %> + + <% end %> +
\ No newline at end of file diff --git a/app/views/admins/departments/shared/_merge_department_modal.html.erb b/app/views/admins/departments/shared/_merge_department_modal.html.erb new file mode 100644 index 000000000..200e75ccd --- /dev/null +++ b/app/views/admins/departments/shared/_merge_department_modal.html.erb @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/update.js.erb b/app/views/admins/departments/update.js.erb new file mode 100644 index 000000000..359bac59c --- /dev/null +++ b/app/views/admins/departments/update.js.erb @@ -0,0 +1,4 @@ +$('.modal.admin-edit-department-modal').modal('hide'); +$.notify({ message: '操作成功' }); + +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file diff --git a/app/views/admins/identity_authentications/index.html.erb b/app/views/admins/identity_authentications/index.html.erb index 170a8fc4a..38b7dfd63 100644 --- a/app/views/admins/identity_authentications/index.html.erb +++ b/app/views/admins/identity_authentications/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/library_applies/index.html.erb b/app/views/admins/library_applies/index.html.erb new file mode 100644 index 000000000..55a7e9e09 --- /dev/null +++ b/app/views/admins/library_applies/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('教学案例发布') %> +<% end %> + +
+ + + <%= form_tag(admins_library_applies_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options) ,class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '编号/名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/library_applies/shared/list', locals: { applies: @library_applies }) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/library_applies/index.js.erb b/app/views/admins/library_applies/index.js.erb new file mode 100644 index 000000000..6f4c3e712 --- /dev/null +++ b/app/views/admins/library_applies/index.js.erb @@ -0,0 +1 @@ +$('.library-applies-list-container').html("<%= j( render partial: 'admins/library_applies/shared/list', locals: { applies: @library_applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/library_applies/shared/_list.html.erb b/app/views/admins/library_applies/shared/_list.html.erb new file mode 100644 index 000000000..c6f9825dc --- /dev/null +++ b/app/views/admins/library_applies/shared/_list.html.erb @@ -0,0 +1,56 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.library.user %> + <% library = apply.library %> + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名教学案例案例描述时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= link_to library.title, library_path(library), :target => "_blank" %><%= overflow_hidden_span library.content[0..50]%><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.reason %><%= t("library_apply.status.#{apply.status}") %> + <%= agree_link '同意', agree_admins_library_apply_path(apply, element: ".library_applies-#{apply.id}"), 'data-confirm': '确认审核通过?' %> + <%= javascript_void_link('拒绝', class: 'action refuse-action', + data: { + toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id, + url: refuse_admins_library_apply_path(apply, element: ".library_applies-#{apply.id}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/edit.html.erb b/app/views/admins/mirror_repositories/edit.html.erb new file mode 100644 index 000000000..7df580c96 --- /dev/null +++ b/app/views/admins/mirror_repositories/edit.html.erb @@ -0,0 +1,8 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) + add_admin_breadcrumb('镜像详情') + end +%> + +<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'update' } %> \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/index.html.erb b/app/views/admins/mirror_repositories/index.html.erb new file mode 100644 index 000000000..ac384408f --- /dev/null +++ b/app/views/admins/mirror_repositories/index.html.erb @@ -0,0 +1,23 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('镜像管理') %> +<% end %> + +
+
+ <%= link_to '新建', new_admins_mirror_repository_path, class: 'btn btn-primary' %> +
+ +<% if @error_mirror_names.present? %> +
+ 以下镜像异常: + <% @error_mirror_names.each do |mirror_name| %> + <%= mirror_name %> + <% end %> +
+<% end %> + +
+ <%= render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } %> +
+ +<%= render 'admins/mirror_repositories/shared/replace_mirror_modal' %> \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/index.js.erb b/app/views/admins/mirror_repositories/index.js.erb new file mode 100644 index 000000000..58ccb1ef8 --- /dev/null +++ b/app/views/admins/mirror_repositories/index.js.erb @@ -0,0 +1 @@ +$('.mirror-repository-list-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } ) %>"); \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/merge.js.erb b/app/views/admins/mirror_repositories/merge.js.erb new file mode 100644 index 000000000..585ecb1af --- /dev/null +++ b/app/views/admins/mirror_repositories/merge.js.erb @@ -0,0 +1,5 @@ +$.notify({ message: '操作成功' },{ type: 'success' }); + +setTimeout(function(){ + window.location.reload(); +}, 500) \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/new.html.erb b/app/views/admins/mirror_repositories/new.html.erb new file mode 100644 index 000000000..792fe0857 --- /dev/null +++ b/app/views/admins/mirror_repositories/new.html.erb @@ -0,0 +1,8 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) + add_admin_breadcrumb('新建镜像') + end +%> + +<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'create' } %> \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/shared/_choose_mirror_modal.html.erb b/app/views/admins/mirror_repositories/shared/_choose_mirror_modal.html.erb new file mode 100644 index 000000000..99c846c70 --- /dev/null +++ b/app/views/admins/mirror_repositories/shared/_choose_mirror_modal.html.erb @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/shared/_form.html.erb b/app/views/admins/mirror_repositories/shared/_form.html.erb new file mode 100644 index 000000000..c8ab2a186 --- /dev/null +++ b/app/views/admins/mirror_repositories/shared/_form.html.erb @@ -0,0 +1,42 @@ +
+ <%= simple_form_for([:admins, mirror], url: { action: form_action }, html: { class: 'edit-mirror col-md-12' }, defaults: { wrapper_html: { class: 'col-md-4' } }) do |f| %> + <% unless mirror.new_record? %> +
+ <%= f.input :mirrorID, label: '镜像ID', input_html: { readonly: true, class: 'form-control-plaintext' } %> + <%= f.input :name, label: '镜像名称', input_html: { readonly: true, class: 'form-control-plaintext' } %> +
+ <% end %> + +
+ <%= f.input :type_name, as: :string, label: '镜像别名 *' %> + +
+ <%= f.label :main_type, label: '类别' %> + <%= f.select :main_type, [['主类别', 1],['小类别', 0]], {}, class: 'form-control optional' %> +
+
+ +
+ <%= f.input :time_limit, as: :integer, label: '评测时限(S)' %> + <%= f.input :resource_limit, as: :integer, label: '磁盘限制(K)' %> +
+ +
+ <%= f.input :cpu_limit, as: :integer, label: 'CPU限制(核)' %> + <%= f.input :memory_limit, as: :integer, label: '内存限制(M)' %> +
+ +
+ <%= f.input :description, as: :text, label: '描述', wrapper_html: { class: 'col-md-8' } %> +
+ +
+ <%= f.input :status, as: :radio_buttons, label: '状态', collection: [%w(未发布 0), %w(已发布 1)], wrapper_html: { class: 'col-md-4' } %> +
+ +
+ <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %> + <%= link_to '取消', admins_mirror_repositories_path, class: 'btn btn-secondary px-4' %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/admins/mirror_repositories/shared/_list.html.erb b/app/views/admins/mirror_repositories/shared/_list.html.erb new file mode 100644 index 000000000..fcf0d03a2 --- /dev/null +++ b/app/views/admins/mirror_repositories/shared/_list.html.erb @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + <% if mirrors.present? %> + <% mirrors.each do |mirror| %> + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
ID类别镜像别名镜像名称镜像描述修改时间脚本状态操作
<%= mirror.id %><%= mirror_type_tag(mirror) %><%= display_text(mirror.type_name) %><%= overflow_hidden_span mirror.name, width: 150 %><%= overflow_hidden_span mirror.description, width: 240 %><%= mirror.updated_at.strftime('%Y-%m-%d %H:%M') %> + <% if mirror.main_type == "1" %> + <%= link_to admins_mirror_repository_mirror_scripts_path(mirror) do %> + + <% end %> + <% end %> + <%= mirror_status_tag mirror %> + <%= link_to '编辑', edit_admins_mirror_repository_path(mirror), class: 'action edit-action' %> + + <% if mirror.status == 2 %> + <%= link_to '同步', new_admins_choose_mirror_repository_path(mirror_id: mirror.id), remote: true, class: 'action sync-action' %> + <% end %> + + <%= javascript_void_link '替换', class: 'action replace-action', data: { toggle: 'modal', target: '.admin-replace-mirror-modal', id: mirror.id, name: mirror.name } %> + + <% if mirror.deletable? %> + <%= delete_link '删除', admins_mirror_repository_path(mirror, element: ".mirror-repository-item-#{mirror.id}"), class: 'delete-mirror-repository-action' %> + <% end %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: mirrors } %> \ No newline at end of file diff --git a/app/views/admins/mirror_repositories/shared/_replace_mirror_modal.html.erb b/app/views/admins/mirror_repositories/shared/_replace_mirror_modal.html.erb new file mode 100644 index 000000000..f2b2d20c8 --- /dev/null +++ b/app/views/admins/mirror_repositories/shared/_replace_mirror_modal.html.erb @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/app/views/admins/mirror_scripts/edit.html.erb b/app/views/admins/mirror_scripts/edit.html.erb new file mode 100644 index 000000000..bc3a6ec91 --- /dev/null +++ b/app/views/admins/mirror_scripts/edit.html.erb @@ -0,0 +1,8 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %> + <% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %> + <% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %> + <% add_admin_breadcrumb('编辑脚本') %> +<% end %> + +<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'update' } %> \ No newline at end of file diff --git a/app/views/admins/mirror_scripts/index.html.erb b/app/views/admins/mirror_scripts/index.html.erb new file mode 100644 index 000000000..f4fa398af --- /dev/null +++ b/app/views/admins/mirror_scripts/index.html.erb @@ -0,0 +1,14 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %> + <% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %> + <% add_admin_breadcrumb('脚本列表') %> +<% end %> + +
+
+ <%= link_to '新建', new_admins_mirror_repository_mirror_script_path(current_mirror), class: 'btn btn-primary' %> +
+ +
+ <%= render partial: 'admins/mirror_scripts/shared/list', locals: { mirror: current_mirror, scripts: @scripts } %> +
diff --git a/app/views/admins/mirror_scripts/new.html.erb b/app/views/admins/mirror_scripts/new.html.erb new file mode 100644 index 000000000..4d6717f41 --- /dev/null +++ b/app/views/admins/mirror_scripts/new.html.erb @@ -0,0 +1,8 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %> + <% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %> + <% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %> + <% add_admin_breadcrumb('新建脚本') %> +<% end %> + +<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'create' } %> \ No newline at end of file diff --git a/app/views/admins/mirror_scripts/shared/_form.html.erb b/app/views/admins/mirror_scripts/shared/_form.html.erb new file mode 100644 index 000000000..c538e9f73 --- /dev/null +++ b/app/views/admins/mirror_scripts/shared/_form.html.erb @@ -0,0 +1,12 @@ +
+ <%= simple_form_for([:admins, mirror, script], url: { action: form_action }, html: { class: 'script-form' }) do |f| %> + <%= f.input :script_type, label: '名称', input_html: { class: 'col-md-6' } %> + <%= f.input :description, as: :text, label: '说明', input_html: { class: 'col-md-6' } %> + <%= f.input :script, as: :text, label: '评测脚本' %> + +
+ <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %> + <%= link_to '取消', admins_mirror_repository_mirror_scripts_path(mirror), class: 'btn btn-secondary px-4' %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/admins/mirror_scripts/shared/_list.html.erb b/app/views/admins/mirror_scripts/shared/_list.html.erb new file mode 100644 index 000000000..30aff38c7 --- /dev/null +++ b/app/views/admins/mirror_scripts/shared/_list.html.erb @@ -0,0 +1,32 @@ + + + + + + + + + + + + <% if scripts.present? %> + <% scripts.each do |script| %> + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
ID名称说明更新时间操作
<%= script.id %><%= overflow_hidden_span script.script_type, width: 200 %><%= overflow_hidden_span script.description, width: 400 %><%= script.updated_at.strftime('%Y-%m-%d %H:%M') %> + <%= link_to '编辑', edit_admins_mirror_repository_mirror_script_path(mirror, script), class: 'action edit-action' %> + + <%= delete_link '删除', admins_mirror_repository_mirror_script_path(mirror, script, element: ".mirror-script-item-#{script.id}"), class: 'delete-mirror-script-action' %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: scripts } %> \ No newline at end of file diff --git a/app/views/admins/myshixuns/index.html.erb b/app/views/admins/myshixuns/index.html.erb new file mode 100644 index 000000000..311c1003f --- /dev/null +++ b/app/views/admins/myshixuns/index.html.erb @@ -0,0 +1,14 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('学员实训列表') %> +<% end %> + +
+ <%= form_tag(admins_myshixuns_path, method: :get, class: 'form-inline search-form', remote: true) do %> + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-md-4 ml-3', placeholder: '学员UID/姓名/学校/实训名称/老师姓名检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/myshixuns/shared/list', locals: { myshixuns: @myshixuns, finish_game_count: @finish_game_count, total_score: @total_score }) %> +
\ No newline at end of file diff --git a/app/views/admins/myshixuns/index.js.erb b/app/views/admins/myshixuns/index.js.erb new file mode 100644 index 000000000..454f0e02a --- /dev/null +++ b/app/views/admins/myshixuns/index.js.erb @@ -0,0 +1 @@ +$('.myshixun-list-container').html("<%= j( render(partial: 'admins/myshixuns/shared/list', locals: { myshixuns: @myshixuns, finish_game_count: @finish_game_count, total_score: @total_score }) ) %>"); \ No newline at end of file diff --git a/app/views/admins/myshixuns/shared/_list.html.erb b/app/views/admins/myshixuns/shared/_list.html.erb new file mode 100644 index 000000000..78e42d58b --- /dev/null +++ b/app/views/admins/myshixuns/shared/_list.html.erb @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + <% if myshixuns.present? %> + <% myshixuns.each do |myshixun| %> + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
ID标识实训名称实训老师完成经验值学员姓名学员单位<%= sort_tag('开启时间', name: 'created_at', path: admins_myshixuns_path) %>
<%= myshixun.id %> + <% current_task = myshixun.last_executable_task || myshixun.last_task %> + <%= link_to "/myshixuns/#{myshixun.identifier}/stages/#{current_task.identifier}", target: '_blank' do %> + <%= myshixun.identifier %> + <% end %> + + <%= link_to "/shixuns/#{myshixun.shixun.identifier}", target: '_blank' do %> + <%= overflow_hidden_span myshixun.shixun.name, width: 280 %> + <% end %> + <%= myshixun.shixun.user.real_name %><%= finish_game_count.fetch(myshixun.id, 0) %>/<%= myshixun.shixun.challenges_count %><%= total_score.fetch(myshixun.id, 0) %> + <%= link_to "/users/#{myshixun.user.login}", target: '_blank' do %> + <%= overflow_hidden_span myshixun.user.real_name, width: 80 %> + <% end %> + <%= overflow_hidden_span myshixun.user&.school_name, width: 150 %><%= myshixun.created_at&.strftime('%Y-%m-%d %H:%M') %>
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: myshixuns } %> \ No newline at end of file diff --git a/app/views/admins/professional_authentications/index.html.erb b/app/views/admins/professional_authentications/index.html.erb index e10d2bd80..32eaa47bd 100644 --- a/app/views/admins/professional_authentications/index.html.erb +++ b/app/views/admins/professional_authentications/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/project_package_applies/index.html.erb b/app/views/admins/project_package_applies/index.html.erb new file mode 100644 index 000000000..912ea3e59 --- /dev/null +++ b/app/views/admins/project_package_applies/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('众包需求发布') %> +<% end %> + +
+ + + <%= form_tag(admins_project_package_applies_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'all'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '需求标题检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/project_package_applies/shared/list', locals: { applies: @package_applies}) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/project_package_applies/index.js.erb b/app/views/admins/project_package_applies/index.js.erb new file mode 100644 index 000000000..fe4d5c782 --- /dev/null +++ b/app/views/admins/project_package_applies/index.js.erb @@ -0,0 +1 @@ +$('.project-package-applies-list-container').html("<%= j( render partial: 'admins/project_package_applies/shared/list', locals: { applies: @package_applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/project_package_applies/shared/_list.html.erb b/app/views/admins/project_package_applies/shared/_list.html.erb new file mode 100644 index 000000000..736d939f9 --- /dev/null +++ b/app/views/admins/project_package_applies/shared/_list.html.erb @@ -0,0 +1,56 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% package = apply.project_package %> + <% user = package.creator %> + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名众包需求需求描述时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= link_to package.title, "/crowdsourcing/#{package.id}", :target => "_blank" %><%= overflow_hidden_span package.content[0..50] %><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.reason %><%= t("admins_apply_status.status.#{apply.status}") %> + <%= agree_link '同意', agree_admins_project_package_apply_path(apply, element: ".project_package_applies-#{apply.id}"), 'data-confirm': '确认审核通过?' %> + <%= javascript_void_link('拒绝', class: 'action refuse-action', + data: { + toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id, + url: refuse_admins_project_package_apply_path(apply, element: ".project_package_applies-#{apply.id}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/shared/403.html.erb b/app/views/admins/shared/403.html.erb new file mode 100644 index 000000000..47e5e9038 --- /dev/null +++ b/app/views/admins/shared/403.html.erb @@ -0,0 +1,6 @@ +
+
+ 403 +
+
未授权
+
\ No newline at end of file diff --git a/app/views/admins/shared/500.html.erb b/app/views/admins/shared/500.html.erb index f053f58ec..b1488d6ff 100644 --- a/app/views/admins/shared/500.html.erb +++ b/app/views/admins/shared/500.html.erb @@ -2,5 +2,5 @@
500
-
系统错误
+
<%= @message %>
\ No newline at end of file diff --git a/app/views/admins/shared/_admin_common_refuse_modal.html.erb b/app/views/admins/shared/_admin_common_refuse_modal.html.erb index a2daf7f0c..10baaef64 100644 --- a/app/views/admins/shared/_admin_common_refuse_modal.html.erb +++ b/app/views/admins/shared/_admin_common_refuse_modal.html.erb @@ -2,20 +2,20 @@ diff --git a/app/views/admins/users/edit.html.erb b/app/views/admins/users/edit.html.erb index 3f9d71b96..22ac1f18e 100644 --- a/app/views/admins/users/edit.html.erb +++ b/app/views/admins/users/edit.html.erb @@ -131,7 +131,7 @@
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %> - <%= link_to '取消', 'javascript:history.go(-1)', class: 'btn btn-secondary px-4' %> + <%= link_to '取消', admins_users_path, class: 'btn btn-secondary px-4' %>
<% end %> \ No newline at end of file diff --git a/app/views/admins/users/index.html.erb b/app/views/admins/users/index.html.erb index 5d2af36c3..b145edd24 100644 --- a/app/views/admins/users/index.html.erb +++ b/app/views/admins/users/index.html.erb @@ -24,7 +24,7 @@ <%= 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') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/users/index.json.jbuilder b/app/views/admins/users/index.json.jbuilder new file mode 100644 index 000000000..5591474a4 --- /dev/null +++ b/app/views/admins/users/index.json.jbuilder @@ -0,0 +1,6 @@ +json.count @users.total_count +json.users do + json.array! @users.each do |user| + json.extract! user, :id, :login, :real_name, :identity, :school_name + end +end \ No newline at end of file diff --git a/app/views/admins/users/shared/_reward_grade_modal.html.erb b/app/views/admins/users/shared/_reward_grade_modal.html.erb index 87c74c499..2cf741906 100644 --- a/app/views/admins/users/shared/_reward_grade_modal.html.erb +++ b/app/views/admins/users/shared/_reward_grade_modal.html.erb @@ -2,7 +2,7 @@