diff --git a/app/controllers/item_baskets_controller.rb b/app/controllers/item_baskets_controller.rb
index dc5367378..03355189d 100644
--- a/app/controllers/item_baskets_controller.rb
+++ b/app/controllers/item_baskets_controller.rb
@@ -12,13 +12,7 @@ class ItemBasketsController < ApplicationController
end
def basket_list
- @single_questions_count = current_user.item_baskets.where(item_type: "SINGLE").count
- @multiple_questions_count = current_user.item_baskets.where(item_type: "MULTIPLE").count
- @judgement_questions_count = current_user.item_baskets.where(item_type: "JUDGMENT").count
- @completion_questions_count = current_user.item_baskets.where(item_type: "COMPLETION").count
- @subjective_questions_count = current_user.item_baskets.where(item_type: "SUBJECTIVE").count
- @practical_questions_count = current_user.item_baskets.where(item_type: "PRACTICAL").count
- @program_questions_count = current_user.item_baskets.where(item_type: "PROGRAM").count
+ @basket_count = current_user.item_baskets.group(:item_type).count
end
def create
diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb
index 30de10b73..7e2a53742 100644
--- a/app/controllers/shixuns_controller.rb
+++ b/app/controllers/shixuns_controller.rb
@@ -1187,7 +1187,10 @@ private
end
def validate_wachat_support
- tip_exception(-2, "..") if (params[:wechat].present? && !@shixun.is_wechat_support?)
+
+ if (params[:wechat].present? && !@shixun.is_wechat_support?)
+ tip_exception(-5, "..")
+ end
end
end
diff --git a/app/models/item_bank.rb b/app/models/item_bank.rb
index 840f488d8..242b7de92 100644
--- a/app/models/item_bank.rb
+++ b/app/models/item_bank.rb
@@ -19,38 +19,26 @@ class ItemBank < ApplicationRecord
end
def apply?
- !public && ApplyAction.where(container_type: "ItemBank", container_id: id, status: 0).exists?
+ !public && ApplyAction.exists?(container_type: "ItemBank", container_id: id, status: 0)
end
def type_string
- result = case item_type
- when "SINGLE"
- "单选题"
- when "MULTIPLE"
- "多选题"
- when "JUDGMENT"
- "判断题"
- when "COMPLETION"
- "填空题"
- when "SUBJECTIVE"
- "简答题"
- when "PRACTICAL"
- "实训题"
- when "PROGRAM"
- "编程题"
- end
- result
+ case item_type
+ when "SINGLE" then "单选题"
+ when "MULTIPLE" then "多选题"
+ when "JUDGMENT" then "判断题"
+ when "COMPLETION" then "填空题"
+ when "SUBJECTIVE" then "简答题"
+ when "PRACTICAL" then "实训题"
+ when "PROGRAM" then "编程题"
+ end
end
def difficulty_string
- result = case difficulty
- when 1
- "简单"
- when 2
- "适中"
- when 3
- "困难"
- end
- result
+ case difficulty
+ when 1 then "简单"
+ when 2 then "适中"
+ when 3 then "困难"
+ end
end
end
diff --git a/app/models/student_work.rb b/app/models/student_work.rb
index 3aee0611b..f8f3bee99 100644
--- a/app/models/student_work.rb
+++ b/app/models/student_work.rb
@@ -223,7 +223,7 @@ class StudentWork < ApplicationRecord
game_score = adjust_score.score
elsif game.present?
setting = homework_common.homework_group_setting game.user_id
- if game.status == 2 && ((game.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && game.end_time && game.end_time < homework_common.late_time))
+ if game.status == 2 && ((game.end_time && setting.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && homework_common.late_time && game.end_time && game.end_time < homework_common.late_time))
answer_open_evaluation = homework_common.homework_detail_manual.answer_open_evaluation
game_score = answer_open_evaluation ? score : (game.final_score > 0 ? game.real_score(score) : 0)
end
diff --git a/app/queries/admins/shixun_settings_query.rb b/app/queries/admins/shixun_settings_query.rb
index 1e45952bf..30a402d1c 100644
--- a/app/queries/admins/shixun_settings_query.rb
+++ b/app/queries/admins/shixun_settings_query.rb
@@ -51,6 +51,7 @@ class Admins::ShixunSettingsQuery < ApplicationQuery
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]
all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip]
+ all_shixuns = all_shixuns.where(is_wechat_support: params[:is_wechat_support]) if params[:is_wechat_support]
custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end
diff --git a/app/views/admins/shixun_settings/index.html.erb b/app/views/admins/shixun_settings/index.html.erb
index 16a02ab96..82d7fa3a1 100644
--- a/app/views/admins/shixun_settings/index.html.erb
+++ b/app/views/admins/shixun_settings/index.html.erb
@@ -71,6 +71,13 @@
只看vip
+
+
+
+
<% end %>
diff --git a/app/views/item_baskets/basket_list.json.jbuilder b/app/views/item_baskets/basket_list.json.jbuilder
index 11db844ab..4cb0d5592 100644
--- a/app/views/item_baskets/basket_list.json.jbuilder
+++ b/app/views/item_baskets/basket_list.json.jbuilder
@@ -1,7 +1,7 @@
-json.single_questions_count @single_questions_count
-json.multiple_questions_count @multiple_questions_count
-json.judgement_questions_count @judgement_questions_count
-json.completion_questions_count @completion_questions_count
-json.subjective_questions_count @subjective_questions_count
-json.practical_questions_count @practical_questions_count
-json.program_questions_count @program_questions_count
+json.single_questions_count @basket_count&.fetch("SINGLE", 0)
+json.multiple_questions_count @basket_count&.fetch("MULTIPLE", 0)
+json.judgement_questions_count @basket_count&.fetch("JUDGMENT", 0)
+json.completion_questions_count @basket_count&.fetch("COMPLETION", 0)
+json.subjective_questions_count @basket_count&.fetch("SUBJECTIVE", 0)
+json.practical_questions_count @basket_count&.fetch("PRACTICAL", 0)
+json.program_questions_count @basket_count&.fetch("PROGRAM", 0)
\ No newline at end of file
diff --git a/lib/tasks/zip_pack.rake b/lib/tasks/zip_pack.rake
index 393f7ab3c..2f11cfe5a 100644
--- a/lib/tasks/zip_pack.rake
+++ b/lib/tasks/zip_pack.rake
@@ -1,51 +1,78 @@
-# 执行示例 bundle exec rake zip_pack:shixun_pack args=123,2323
+# 执行示例 bundle exec rake zip_pack:shixun_pack class=Course ids=123,2323
+# 执行示例 bundle exec rake zip_pack:shixun_pack class=HomeworkCommon ids=123,2323
namespace :zip_pack do
desc "手工打包作品"
OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip"
task :shixun_pack => :environment do
- if ENV['args']
- homework_ids = ENV['args'].split(",").map(&:to_i)
- homeworks = HomeworkCommon.where(id: homework_ids)
- homeworks.includes(:score_student_works).each do |homework|
- out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip"
- out_file_name.gsub!(" ", "-")
- out_file_name.gsub!("/", "_")
+ if ENV['class'] && ENV['ids']
+ env_ids = ENV['ids'].split(",").map(&:to_i)
+ folders = []
+ if ENV['class'] == "Course"
+ courses = Course.where(id: env_ids)
+ courses.each do |course|
+ homeworks = course.practice_homeworks.homework_published
+ new_dir_name = "#{course.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
+ new_dir_name.gsub!(" ", "-")
+ new_dir_name.gsub!("/", "_")
+ new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
+ zip_homework_pdf homeworks, new_folder
+ folders << new_folder
+ end
+ else
+ homeworks = HomeworkCommon.where(id: env_ids)
+ new_dir_name = "#{homeworks.first&.course&.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
+ new_dir_name.gsub!(" ", "-")
+ new_dir_name.gsub!("/", "_")
+ new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
+ zip_homework_pdf homeworks, new_folder
+ folders << new_folder
+ end
+
+ puts "下载路径: #{folders.join(",")}"
+ end
+ end
- zipfile_name = "#{OUTPUT_FOLDER}/#{out_file_name}"
- Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
+ def zip_homework_pdf homeworks, folder
+ Dir.mkdir(folder) unless File.directory?(folder)
- student_works = homework.score_student_works
+ homeworks.includes(:score_student_works).each do |homework|
+ out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip"
+ out_file_name.gsub!(" ", "-")
+ out_file_name.gsub!("/", "_")
- if student_works.size > 0
- pdfs = []
- Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
- student_works.find_each.map do |student_work|
- export = ExportShixunReportService.new(homework, student_work)
- pdf = export.to_pdf
- pdfs << pdf
- begin
- zip.add(export.filename, pdf.path)
- puts "out: #{export.filename}_#{pdf.path}"
- rescue => ex
- Rails.logger.error(ex.message)
+ zipfile_name = "#{folder}/#{out_file_name}"
+ # Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
- zip.get_output_stream('FILE_NOTICE.txt'){|os| os.write("文件重复:#{export.filename}") }
- next
- end
+ student_works = homework.score_student_works
+
+ if student_works.size > 0
+ pdfs = []
+ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
+ student_works.find_each.map do |student_work|
+ export = ExportShixunReportService.new(homework, student_work)
+ pdf = export.to_pdf
+ pdfs << pdf
+ begin
+ zip.add(export.filename, pdf.path)
+ puts "out: #{export.filename}_#{pdf.path}"
+ rescue => ex
+ Rails.logger.error(ex.message)
+
+ zip.get_output_stream('FILE_NOTICE.txt'){|os| os.write("文件重复:#{export.filename}") }
+ next
end
end
- zipfile = zipfile_name
- else
- zipfile = {:message => "no file"}
end
- puts "out: #{zipfile}"
-
+ zipfile = zipfile_name
+ else
+ zipfile = {:message => "no file"}
end
end
end
+ # 执行示例 bundle exec rake zip_pack:homework_attach_pack args=123
task :homework_attach_pack => :environment do
include ExportHelper
if ENV['args']
@@ -61,7 +88,4 @@ namespace :zip_pack do
end
end
- def filename_for_content_disposition(name)
- request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name
- end
end
\ No newline at end of file
diff --git a/public/react/package.json b/public/react/package.json
index 25aa1d87f..a30780480 100644
--- a/public/react/package.json
+++ b/public/react/package.json
@@ -75,6 +75,7 @@
"react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1",
+ "react-cookies": "^0.1.1",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0",
diff --git a/public/react/src/App.js b/public/react/src/App.js
index 247e7939b..96a4f91b0 100644
--- a/public/react/src/App.js
+++ b/public/react/src/App.js
@@ -366,6 +366,11 @@ const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'),
loading: Loading
});
+// 微信代码编辑器
+const WXCode = Loadable({
+ loader: () => import('./modules/wxcode'),
+ loading: Loading
+});
// //个人竞赛报名
// const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@@ -823,6 +828,11 @@ class App extends Component {
render={
(props) => ()
}/>
+ ()
+ }
+ />
{
+
+ const {
+ isShow,
+ wxCode,
+ path,
+ showLoading,
+ // userCode,
+ testCase = [],
+ getWXCode,
+ last_compile_output,
+ test_sets_count,
+ sets_error_count,
+ getWXCodeTestCase,
+ restoreWXCode,
+ updateWXCodeForEditor,
+ updateWXCodeForInterval,
+ evaluateWxCode,
+ showWXCodeTextCase,
+ changeWXCodeEvaluateLoading
+ } = props;
+
+ const {identifier} = props.match.params;
+ // 获取路径参数
+ const _params = window.location.search;
+ if (_params) {
+ let _search = _params.split('?')[1];
+ _search.split('&').forEach(item => {
+ console.log(item);
+ const _arr = item.split('=');
+ cookie.save(_arr[0], _arr[1], {
+ path: '/',
+ domain: '.educoder.net'
+ });
+ });
+ }
+ const [isActive, setIsActive] = useState(-1);
+ // const [isVisible, setIsVisible] = useState(false);
+ const editorRef = useRef(null);
+ let timer = null;
+ useEffect(() => {
+ // 加载代码块内容
+ getWXCode(identifier);
+ // 加载测试集
+ const params = {
+ path,
+ status: 0,
+ retry: 1
+ };
+ getWXCodeTestCase(identifier, params);
+ }, []);
+ // 关闭
+ const handleCloseTestCase = () => {
+ // setIsVisible(false);
+ showWXCodeTextCase(false)
+ }
+ // 测试集
+ const handleClickTestCase = () => {
+ // setIsVisible(true);
+ showWXCodeTextCase(true)
+ }
+ // 编辑器代码
+ const handleEditorChange = (origin, monaco) => {
+ editorRef.current = monaco; // 获取当前monaco实例
+ // setEditCode(origin); // 保存编辑器初始值
+ editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
+ // TODO 需要优化 节流
+ const val = editorRef.current.getValue();
+ // console.log('编辑器代码====>>>>', val);
+ // updateWXCodeForEditor(val);
+ codeChange(val);
+ });
+ };
+
+ const codeChange = (code) => {
+ // console.log(code);
+ updateWXCodeForEditor(code);
+ if (!timer) {
+ timer = setInterval(function () {
+ clearInterval(timer);
+ timer = null;
+ // 调用更新代码
+ updateWXCodeForInterval(identifier, path);
+ }, 10000);
+ }
+ }
+
+ // 关闭单个测试集
+ const handleCloseItem = (i, flag) => {
+ if (!flag) return;
+ setIsActive(isActive === i ? -1 : i);
+ }
+ // 初始化
+ const handleResetCode = () => {
+ const result = window.confirm('你在本文件中修改的内容将丢失, 是否确定重新加载初始代码?');
+ if (result) {
+ identifier && restoreWXCode(identifier, { path });
+ }
+ }
+ // 评测
+ const handleEvalateCode = () => {
+ changeWXCodeEvaluateLoading(true);
+ evaluateWxCode(identifier, path);
+ }
+
+ const tcclasses = isShow ? `wx-code-test-case active` : 'wx-code-test-case';
+ const loading = showLoading ? 'code-evaluate-loading active' : 'code-evaluate-loading';
+ const _val = sets_error_count === 0;
+ let resultTxt = (_val) ? '全部通过' : `${sets_error_count}组测试结果不匹配`;
+ const iclasses = _val ? 'iconfont icon-tishi1 icon success' : 'iconfont icon-tishi1 icon fail';
+ const tclasses = _val ? 'result-txt success' : 'result-txt fail';
+ const ulClasses = last_compile_output ? 'case-list hasResult' : 'case-list';
+ return (
+
+
+
+
+
+
+
+
+
+ 初始化
+
+
+
+ 测试集
+
+
+ {/*
*/}
+
+
+
+ {/* 测试集 */}
+
+
+
+ 共{testCase.length}个测试用例
+ 关闭
+
+
+
+ {test_sets_count - sets_error_count}/{test_sets_count}
+ {resultTxt}
+
+
+ {
+ testCase.map((item, i) => {
+ const {input, output, actual_output, is_public, result} = item;
+ const _classes = isActive === i ? 'case-item-desc active' : 'case-item-desc';
+ const iconclasses = isActive === i ? 'iconfont icon-sanjiaoxing-down icon active' : 'iconfont icon-triangle icon';
+ const headerClasses = is_public ? 'item-header-desc active' : 'item-header-desc';
+ // console.log(_classes);
+ return (
+ -
+
handleCloseItem(i, is_public)}>
+
+
+ 测试集{i + 1}
+
+ {
+ is_public
+ ? (result
+ ?
+ : )
+ : (
+ 隐藏测试集,暂不支持解锁和查看
+ {/* {result
+ ?
+ :
+ } */}
+ )
+ }
+
+
+
+ 测试输入
+ {input || '-'}
+ 预期输出
+ {/* */}
+
+ {/* */}
+ 实际输出
+
+
+
+ )
+ })
+ }
+
+
+
+ {/* 测评中 */}
+
+
+
+ 测评中...
+
+
+
+ );
+}
+
+const mapStateToProps = (state) => {
+ const {
+ path,
+ isShow,
+ wxCode,
+ userCode,
+ testCase,
+ showLoading,
+ last_compile_output,
+ test_sets_count,
+ sets_error_count
+ } = state.wxcodeReducer;
+ console.log(state);
+ return {
+ path,
+ isShow,
+ wxCode,
+ userCode,
+ testCase,
+ showLoading,
+ last_compile_output,
+ test_sets_count,
+ sets_error_count
+ };
+}
+
+const mapDispatchToProps = (dispatch) => ({
+ getWXCode: (identifier) => dispatch(actions.getWXCode(identifier)),
+ getWXCodeTestCase: (identifier, params) => dispatch(actions.getWXCodeTestCase(identifier, params)),
+ restoreWXCode: (identifier, params) => dispatch(actions.restoreWXCode(identifier, params)),
+ updateWXCodeForEditor: (code) => dispatch(actions.updateWXCodeForEditor(code)),
+ updateWXCodeForInterval: (identifier, path) => dispatch(actions.updateWXCodeForInterval(identifier, path)),
+ evaluateWxCode: (identifier, path) => dispatch(actions.evaluateWxCode(identifier, path)),
+ showWXCodeTextCase: (flag) => dispatch(actions.showWXCodeTextCase(flag)),
+ changeWXCodeEvaluateLoading: (flag) => dispatch(actions.changeWXCodeEvaluateLoading(flag))
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(App);
diff --git a/public/react/src/modules/wxcode/index.scss b/public/react/src/modules/wxcode/index.scss
new file mode 100644
index 000000000..f831da519
--- /dev/null
+++ b/public/react/src/modules/wxcode/index.scss
@@ -0,0 +1,275 @@
+.wx-code-area{
+ height: 100vh;
+ overflow: hidden;
+ .wx-code-flex{
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ height: 100%;
+ }
+ .wx-code-item{
+ flex: 1;
+ }
+ .wx-code-test{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 120px;
+ background-color: #052645;
+ padding: 0 50px;
+ }
+
+ .flex-btn{
+ display: flex;
+ .icon-btn{
+ display: flex;
+ flex-direction: column;
+ color: #2EA4FF;
+ align-items: center;
+ .icon{
+ // font-size: 24px !important;
+ transform: scale(2);
+ line-height: 2;
+ }
+ .icon-reset{
+ transform: scale(2.4);
+ }
+ // .icon:first-child{
+ // transform: scale((3));
+ // }
+
+ &:last-child{
+ margin-left: 50px;
+ }
+ .icon-txt{
+ font-size: 24px;
+ }
+ }
+ }
+
+ .wx-pt-btn{
+ border: none;
+ outline: none;
+ border-radius: 9999px;
+ padding: 0 40px;
+ line-height: 72px;
+ font-size: 32px;
+ color: #fff;
+ background:#2EA4FF;
+ }
+
+ .wx-code-test-case{
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ top: 0;
+ transform: translateY(100%);
+ transition: transform, opacity .3s;
+ opacity: 0;
+ // border-top-left-radius: 16px;
+ // border-top-right-radius: 16px;
+
+ &::before{
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ content: '';
+ background: rgba(0,0,0,.6);
+ }
+ .text-case-list{
+ position: absolute;
+ width: 100%;
+ bottom: 0;
+ top: 150px;
+ background:rgba(1,14,31,1);
+ border-top-left-radius: 16px;
+ border-top-right-radius: 16px;
+ border:2px solid rgba(33,56,87,1);
+ color: #fff;
+ }
+
+ .list-header{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 88px;
+ padding: 30px;
+ box-sizing: border-box;
+ border-bottom: 2px solid #213857;
+ .header-title{
+ color:#637DA6;
+ font-size: 24px;
+ }
+ .header-close{
+ font-size: 28px;
+ color: #2EA4FF;
+ }
+ }
+
+ .wxcode-test-result{
+ display: flex;
+ height: 72px;
+ // background: gold;
+ align-items: center;
+ padding: 0 30px;
+ .success{
+ color: rgba(68,209,95,1);
+ }
+ .fail{
+ color: rgba(196, 79, 78, 1);
+ }
+ .icon{
+ font-size: 36px !important;
+ position: relative;
+ top: -8px;
+ margin-right: 10px;
+ }
+ .result-txt{
+ font-size: 34px;
+ }
+ .result-txt-desc{
+ max-width: 500px;
+ overflow: hidden;
+ text-overflow:ellipsis;
+ white-space: nowrap;
+ margin: 20px;
+ }
+ }
+
+ .case-list{
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0px;
+ overflow: auto;
+ margin-top: 88px;
+ padding: 0 30px 30px;
+ // margin: 88px 30px 0;
+ .case-item{
+ position: relative;
+ margin-top: 30px;
+ border-radius: 8px;
+ background:rgba(23,39,64,1);
+ padding: 30px;
+ }
+
+ &.hasResult{
+ margin-top: 170px;
+
+ .case-item:first-child{
+ margin-top: 0;
+ }
+ }
+
+ .case-item-header{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ .case_item_success,
+ .case_item_fail{
+ font-size: 24px !important;
+ transform: scale(2);
+ }
+ .case_item_success{
+ color: rgba(68,209,95,1);
+ }
+ .case_item_fail{
+ color: rgba(196, 79, 78, 1);
+ }
+ .case-item-tips{
+ color: #C67676;
+ font-size: 22px;
+ }
+ }
+
+ .item-header-desc{
+ font-size: 32px;
+ color: #405D8C;
+ line-height: 1.5;
+ // background: gold;
+ .icon{
+ position: relative;
+ top: -4px;
+ font-size: 32px !important;
+ margin-right: 10px;
+ }
+ &.active{
+ color: #fff;
+ }
+ }
+
+ .case-item-tips{
+ font-size: 28px;
+ }
+ .case-item-desc{
+ display: none;
+ flex-direction: column;
+ font-size: 28px;
+ line-height: 1.5;
+
+ &.active{
+ display: flex;
+ }
+ }
+ .desc-title{
+ color: #637DA6;
+ line-height: 2;
+ }
+ .text-area-style{
+ background:#010E1F !important;
+ color: #fff;
+ font-size: 28px;
+ line-height: 1.5;
+ border: none;
+ }
+ }
+
+ &.active{
+ transform: translateY(0);
+ opacity: 1;
+ // .item-header-desc{
+ // color: #fff;
+ // }
+ }
+ }
+ .code-evaluate-loading{
+ display: none;
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ justify-content: center;
+ align-items: center;
+ &::before{
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ content: '';
+ background: rgba(0,0,0,.5);
+ }
+
+ .loading-flex{
+ display: flex;
+ position: absolute;
+ flex-direction: column;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ color: #fff;
+ .loading-icon{
+ font-size: 100px !important;
+ }
+ .loading-txt{
+ font-size: 32px;
+ }
+ }
+
+ &.active{
+ display: flex;
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/react/src/redux/actions/actionTypes.js b/public/react/src/redux/actions/actionTypes.js
index e3c2f65a9..f749cd7d8 100644
--- a/public/react/src/redux/actions/actionTypes.js
+++ b/public/react/src/redux/actions/actionTypes.js
@@ -92,7 +92,14 @@ const types = {
/** 统计 */
GET_STATIC_INFO: 'GET_STATIC_INFO',
CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS',
- CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL'
+ CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL',
+ /** 微信 */
+ GET_WXCODE_BY_IDENTIFIER: 'GET_WXCODE_BY_IDENTIFIER',
+ GET_WXCODE_TEST_CASE: 'GET_WXCODE_TEST_CASE',
+ UPDATE_WXCODE_BY_IDENTIFIER: 'UPDATE_WXCODE_BY_IDENTIFIER',
+ UPDATE_WXCODE_FOR_EDITOR: 'UPDATE_WXCODE_FOR_EDITOR',
+ IS_SHOW_WXCODE_TEST_CASES: 'IS_SHOW_WXCODE_TEST_CASES',
+ SHOW_WX_CODE_LOADING: 'SHOW_WX_CODE_LOADING'
}
export default types;
diff --git a/public/react/src/redux/actions/index.js b/public/react/src/redux/actions/index.js
index f7b7a41d9..3b8bb2e12 100644
--- a/public/react/src/redux/actions/index.js
+++ b/public/react/src/redux/actions/index.js
@@ -12,6 +12,7 @@ import {
changePaginationInfo,
deleteItem
} from './ojList';
+
import {
validateOjForm,
saveOjFormCode,
@@ -109,6 +110,16 @@ import {
initTotal
} from './static';
+import {
+ getWXCode,
+ getWXCodeTestCase,
+ restoreWXCode,
+ updateWXCodeForEditor,
+ updateWXCodeForInterval,
+ evaluateWxCode,
+ showWXCodeTextCase,
+ changeWXCodeEvaluateLoading
+} from './wxCode';
export default {
toggleTodo,
getOJList,
@@ -191,5 +202,14 @@ export default {
// 统计
staticList,
changeParams,
- initTotal
+ initTotal,
+ // 微信
+ getWXCode,
+ getWXCodeTestCase,
+ restoreWXCode,
+ updateWXCodeForEditor,
+ updateWXCodeForInterval,
+ evaluateWxCode,
+ showWXCodeTextCase,
+ changeWXCodeEvaluateLoading
}
\ No newline at end of file
diff --git a/public/react/src/redux/actions/wxCode.js b/public/react/src/redux/actions/wxCode.js
new file mode 100644
index 000000000..32927658d
--- /dev/null
+++ b/public/react/src/redux/actions/wxCode.js
@@ -0,0 +1,196 @@
+/*
+ * @Description:
+ * @Author: tangjiang
+ * @Github:
+ * @Date: 2020-01-15 15:41:10
+ * @LastEditors : tangjiang
+ * @LastEditTime : 2020-01-16 15:25:25
+ */
+import types from './actionTypes.js';
+import {
+ fetchWxCode,
+ fetchWxCodeTextCase,
+ fetchRestoreWxCode,
+ fetchUpdateWxCode,
+ fetchWxCodeGameBuild,
+ fetchWxCodeGameStatus
+} from '../../services/wxcodeService.js';
+
+// 加载代码块
+export const getWXCode = (identifier) => {
+ return (dispatch) => {
+ fetchWxCode(identifier).then(res => {
+ if (res.status === 200) {
+ dispatch({
+ type: types.GET_WXCODE_BY_IDENTIFIER,
+ payload: res.data.content
+ });
+ }
+ });
+ }
+};
+
+// 加载测试集
+export const getWXCodeTestCase = (identifier, params) => {
+ return (dispatch) => {
+ fetchWxCodeTextCase(identifier, params).then(res => {
+ // console.log('加载测试集: ====>>>>>>', res);
+ try{
+ const {data = {}} = res;
+ console.log(data.test_sets);
+ dispatch({
+ type: types.GET_WXCODE_TEST_CASE,
+ payload: {
+ test_sets: data.test_sets || [],
+ game_id: data.game && data.game.id,
+ myIdentifier: data.myshixun.identifier,
+ exec_time: data.challenge.exec_time,
+ path: data.challenge.path,
+ last_compile_output: data.last_compile_output,
+ test_sets_count: data.test_sets_count,
+ sets_error_count: data.sets_error_count
+ }
+ });
+ } catch(err) {
+ console.log(err);
+ };
+ });
+ }
+}
+
+// 初始化
+export const restoreWXCode = (identifier, params) => {
+ return (dispatch) => {
+ fetchRestoreWxCode(identifier, params).then(res => {
+ console.log('点击了初始化代码: ', res);
+ const {data} = res;
+ dispatch({
+ type: types.GET_WXCODE_BY_IDENTIFIER,
+ payload: data.content || ''
+ });
+ });
+ }
+}
+
+// 更新编辑器代码
+export const updateWXCodeForEditor = (code) => {
+ return {
+ type: types.UPDATE_WXCODE_FOR_EDITOR,
+ payload: code
+ }
+}
+
+export const updateWxCode = (path, identifier, userCode, game_id, evaluate = 0,) => {
+ return fetchUpdateWxCode(identifier, {
+ path,
+ evaluate,
+ content: userCode,
+ game_id
+ });
+}
+// 定时更新代码内容
+export const updateWXCodeForInterval = (identifier, path) => {
+ return (dispatch, getState) => {
+ const {wxCode, userCode, game_id, myIdentifier} = getState().wxcodeReducer;
+ if (wxCode !== userCode) {
+ updateWxCode(path, myIdentifier, userCode, game_id, 0);
+ }
+ }
+}
+
+// 评测
+export const evaluateWxCode = (identifier, path) => {
+ return (dispatch, getState) => {
+ const {
+ userCode,
+ game_id,
+ myIdentifier,
+ exec_time,
+ path,
+ last_compile_output,
+ test_sets_count,
+ sets_error_count
+ } = getState().wxcodeReducer;
+ updateWxCode(path, myIdentifier, userCode, game_id, 1).then(res => {
+ // build
+ // const {} = res;
+ console.log(res);
+ const params = {
+ content_modified: 1,
+ sec_key: res.data.sec_key
+ }
+ console.log(params);
+ fetchWxCodeGameBuild(identifier, params).then(res => {
+ if (res.data.status === 1) {
+ // 定时调用 game_status fetchWxCodeGameStatus
+ let count = 1;
+ const intervalTime = 500;
+ function wxCodeGameStatus (intervalTime, finalTime, count, timer) {
+ const excuteTime = (count++) * intervalTime; // 当前执行时间
+ console.log(finalTime, count, excuteTime);
+ fetchWxCodeGameStatus(identifier).then(r => {
+ const { status, test_sets = [] } = r.data;
+ if (+status > -1 || ((excuteTime / 1000) > (finalTime + 1))) {
+ clearInterval(timer);
+ timer = null;
+ dispatch({
+ type: types.SHOW_WX_CODE_LOADING,
+ payload: false
+ });
+ setTimeout(() => {
+ // 显示测试集弹框
+ dispatch({
+ type: types.IS_SHOW_WXCODE_TEST_CASES,
+ payload: true
+ });
+ dispatch({
+ type: types.GET_WXCODE_TEST_CASE,
+ payload: {
+ test_sets,
+ game_id,
+ myIdentifier,
+ exec_time,
+ path,
+ last_compile_output,
+ test_sets_count,
+ sets_error_count
+ }
+ });
+ }, 50);
+ }
+ }).catch(err => {
+ dispatch({
+ type: types.SHOW_WX_CODE_LOADING,
+ payload: false
+ });
+ });
+ }
+ let timer = setInterval(() => {
+ wxCodeGameStatus(intervalTime, exec_time, count++, timer);
+ }, intervalTime);
+ }
+ })
+ }).catch(err => {
+ dispatch({
+ type: types.SHOW_WX_CODE_LOADING,
+ payload: false
+ });
+ });
+ }
+}
+
+// 显示测试集
+export const showWXCodeTextCase = (flag) => {
+ return {
+ type: types.IS_SHOW_WXCODE_TEST_CASES,
+ payload: flag
+ }
+}
+
+// 显示测评中的状态
+export const changeWXCodeEvaluateLoading = (flag) => {
+ return {
+ type: types.SHOW_WX_CODE_LOADING,
+ payload: flag
+ }
+}
\ No newline at end of file
diff --git a/public/react/src/redux/reducers/index.js b/public/react/src/redux/reducers/index.js
index 6506cf584..afb58078c 100644
--- a/public/react/src/redux/reducers/index.js
+++ b/public/react/src/redux/reducers/index.js
@@ -17,6 +17,7 @@ import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer';
import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer';
+import wxcodeReducer from './wxcodeReducer';
export default combineReducers({
testReducer,
@@ -28,5 +29,6 @@ export default combineReducers({
jupyterReducer,
commentReducer,
tpiReducer,
- staticReducer
+ staticReducer,
+ wxcodeReducer
});
diff --git a/public/react/src/redux/reducers/wxcodeReducer.js b/public/react/src/redux/reducers/wxcodeReducer.js
new file mode 100644
index 000000000..26ac4204f
--- /dev/null
+++ b/public/react/src/redux/reducers/wxcodeReducer.js
@@ -0,0 +1,68 @@
+/*
+ * @Description:
+ * @Author: tangjiang
+ * @Github:
+ * @Date: 2020-01-15 15:37:44
+ * @LastEditors : tangjiang
+ * @LastEditTime : 2020-01-16 15:22:37
+ */
+import types from "../actions/actionTypes";
+const initialState = {
+ wxCode: '',
+ userCode: '',
+ testCase: [],
+ game_id: '',
+ myIdentifier: '',
+ exec_time: 0,
+ last_compile_output: '',
+ test_sets_count: 0,
+ sets_error_count: 0,
+ path: '',
+ isShow: false,
+ showLoading: false
+};
+
+const wxcodeReducer = (state = initialState, action) => {
+ const { payload, type } = action;
+ switch (type) {
+ case types.GET_WXCODE_BY_IDENTIFIER:
+ return {
+ ...state,
+ wxCode: payload,
+ userCode: payload
+ }
+ case types.GET_WXCODE_TEST_CASE:
+ return {
+ ...state,
+ testCase: payload.test_sets,
+ game_id: payload.game_id,
+ myIdentifier: payload.myIdentifier,
+ exec_time: payload.exec_time,
+ path: payload.path,
+ last_compile_output: payload.last_compile_output,
+ test_sets_count: payload.test_sets_count,
+ sets_error_count: payload.sets_error_count
+ }
+ case types.UPDATE_WXCODE_FOR_EDITOR:
+ return {
+ ...state,
+ userCode: payload
+ }
+ case types.IS_SHOW_WXCODE_TEST_CASES:
+ return {
+ ...state,
+ isShow: payload
+ }
+ case types.SHOW_WX_CODE_LOADING:
+ return {
+ ...state,
+ showLoading: payload
+ }
+ default:
+ return {
+ ...state
+ }
+ }
+}
+
+export default wxcodeReducer;
diff --git a/public/react/src/services/wxcodeService.js b/public/react/src/services/wxcodeService.js
new file mode 100644
index 000000000..4c44b259e
--- /dev/null
+++ b/public/react/src/services/wxcodeService.js
@@ -0,0 +1,50 @@
+/*
+ * @Description:
+ * @Author: tangjiang
+ * @Github:
+ * @Date: 2020-01-15 15:44:36
+ * @LastEditors : tangjiang
+ * @LastEditTime : 2020-01-17 10:06:45
+ */
+import axios from 'axios';
+
+// 获取代码块
+export async function fetchWxCode (identifier, params) {
+ const url = `/tasks/${identifier}/rep_content.json`;
+ params = Object.assign({}, params, {withCredentials: true});
+ return axios.get(url, {params});
+}
+
+// 获取测试值
+export async function fetchWxCodeTextCase (identifier) {
+ const url = `/tasks/${identifier}.json`;
+ const params = Object.assign({}, {withCredentials: true});
+ return axios.get(url, {params});
+}
+
+// 更新代码块内容
+export async function fetchUpdateWxCode (identifier, params) {
+ // /myshixuns/8etu3pilsa/update_file.json
+ const url = `/myshixuns/${identifier}/update_file.json`;
+ params = Object.assign({}, params, {withCredentials: true});
+ return axios.post(url, params);
+}
+
+// 恢复初始化
+export async function fetchRestoreWxCode (identifier, params) {
+ const url = `/tasks/${identifier}/reset_original_code.json`;
+ params = Object.assign({}, params, {withCredentials: true});
+ return axios.get(url, {params});
+}
+// 评测
+export async function fetchWxCodeGameBuild (identifier, params) {
+ const url = `/tasks/${identifier}/game_build.json`;
+ params = Object.assign({}, params, {withCredentials: true});
+ return axios.get(url, {params});
+}
+
+export async function fetchWxCodeGameStatus (identifier) {
+ const url = `/tasks/${identifier}/game_status.json`;
+ const params = Object.assign({}, {withCredentials: true});
+ return axios.get(url, {params});
+}