Merge branch 'dev_aliyun' into develop

dev_tj
cxt 5 years ago
commit 3225f0a45d

@ -12,13 +12,7 @@ class ItemBasketsController < ApplicationController
end end
def basket_list def basket_list
@single_questions_count = current_user.item_baskets.where(item_type: "SINGLE").count @basket_count = current_user.item_baskets.group(:item_type).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
end end
def create def create

@ -1187,7 +1187,10 @@ private
end end
def validate_wachat_support 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
end end

@ -19,38 +19,26 @@ class ItemBank < ApplicationRecord
end end
def apply? 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 end
def type_string def type_string
result = case item_type case item_type
when "SINGLE" when "SINGLE" then "单选题"
"单选题" when "MULTIPLE" then "多选题"
when "MULTIPLE" when "JUDGMENT" then "判断题"
"多选题" when "COMPLETION" then "填空题"
when "JUDGMENT" when "SUBJECTIVE" then "简答题"
"判断题" when "PRACTICAL" then "实训题"
when "COMPLETION" when "PROGRAM" then "编程题"
"填空题" end
when "SUBJECTIVE"
"简答题"
when "PRACTICAL"
"实训题"
when "PROGRAM"
"编程题"
end
result
end end
def difficulty_string def difficulty_string
result = case difficulty case difficulty
when 1 when 1 then "简单"
"简单" when 2 then "适中"
when 2 when 3 then "困难"
"适中" end
when 3
"困难"
end
result
end end
end end

@ -223,7 +223,7 @@ class StudentWork < ApplicationRecord
game_score = adjust_score.score game_score = adjust_score.score
elsif game.present? elsif game.present?
setting = homework_common.homework_group_setting game.user_id 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 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) game_score = answer_open_evaluation ? score : (game.final_score > 0 ? game.real_score(score) : 0)
end end

@ -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(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(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(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]) custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end end

@ -71,6 +71,13 @@
<span class="only_view">只看vip</span> <span class="only_view">只看vip</span>
</label> </label>
</div> </div>
<div class="mr-5">
<label for="is_wechat_support">
<%= check_box_tag :is_wechat_support, !@sort_json[:is_wechat_support],@sort_json[:is_wechat_support], class:"shixun-settings-select" %>
<span class="only_view">只看小程序可用</span>
</label>
</div>
</div> </div>
</div> </div>
<% end %> <% end %>

@ -1,7 +1,7 @@
json.single_questions_count @single_questions_count json.single_questions_count @basket_count&.fetch("SINGLE", 0)
json.multiple_questions_count @multiple_questions_count json.multiple_questions_count @basket_count&.fetch("MULTIPLE", 0)
json.judgement_questions_count @judgement_questions_count json.judgement_questions_count @basket_count&.fetch("JUDGMENT", 0)
json.completion_questions_count @completion_questions_count json.completion_questions_count @basket_count&.fetch("COMPLETION", 0)
json.subjective_questions_count @subjective_questions_count json.subjective_questions_count @basket_count&.fetch("SUBJECTIVE", 0)
json.practical_questions_count @practical_questions_count json.practical_questions_count @basket_count&.fetch("PRACTICAL", 0)
json.program_questions_count @program_questions_count json.program_questions_count @basket_count&.fetch("PROGRAM", 0)

@ -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 namespace :zip_pack do
desc "手工打包作品" desc "手工打包作品"
OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip" OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip"
task :shixun_pack => :environment do task :shixun_pack => :environment do
if ENV['args'] if ENV['class'] && ENV['ids']
homework_ids = ENV['args'].split(",").map(&:to_i) env_ids = ENV['ids'].split(",").map(&:to_i)
homeworks = HomeworkCommon.where(id: homework_ids) folders = []
homeworks.includes(:score_student_works).each do |homework| if ENV['class'] == "Course"
out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip" courses = Course.where(id: env_ids)
out_file_name.gsub!(" ", "-") courses.each do |course|
out_file_name.gsub!("/", "_") 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}" def zip_homework_pdf homeworks, folder
Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name)) 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 zipfile_name = "#{folder}/#{out_file_name}"
pdfs = [] # Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
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}") } student_works = homework.score_student_works
next
end 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
end end
zipfile = zipfile_name
else
zipfile = {:message => "no file"}
end end
puts "out: #{zipfile}" zipfile = zipfile_name
else
zipfile = {:message => "no file"}
end end
end end
end end
# 执行示例 bundle exec rake zip_pack:homework_attach_pack args=123
task :homework_attach_pack => :environment do task :homework_attach_pack => :environment do
include ExportHelper include ExportHelper
if ENV['args'] if ENV['args']
@ -61,7 +88,4 @@ namespace :zip_pack do
end end
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 end

@ -75,6 +75,7 @@
"react-codemirror": "^1.0.0", "react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0", "react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1", "react-content-loader": "^3.1.1",
"react-cookies": "^0.1.1",
"react-dev-utils": "^5.0.0", "react-dev-utils": "^5.0.0",
"react-dom": "^16.9.0", "react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0", "react-hot-loader": "^4.0.0",

@ -366,6 +366,11 @@ const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'), loader: () => import('./modules/tpm/jupyter'),
loading: Loading loading: Loading
}); });
// 微信代码编辑器
const WXCode = Loadable({
loader: () => import('./modules/wxcode'),
loading: Loading
});
// //个人竞赛报名 // //个人竞赛报名
// const PersonalCompetit = Loadable({ // const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'), // loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -823,6 +828,11 @@ class App extends Component {
render={ render={
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />) (props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
}/> }/>
<Route path="/wxcode/:identifier?" component={WXCode}
render={
(props)=>(<WXCode {...this.props} {...props} {...this.state}></WXCode>)
}
/>
<Route exact path="/" <Route exact path="/"
// component={ShixunsHome} // component={ShixunsHome}
render={ render={

@ -0,0 +1,285 @@
/*
* @Description: 微信端代码编辑器
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 09:56:34
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-17 11:48:21
*/
import './index.scss';
import React, {useState, useEffect, useRef} from 'react';
import MonacoEditor from '@monaco-editor/react';
import { Input, Icon } from 'antd';
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import cookie from 'react-cookies'
const { TextArea } = Input;
const App = (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 (
<div className="wx-code-area">
<div className="wx-code-flex">
<div className="wx-code-item">
<MonacoEditor
height="100%"
width="100%"
language="python"
value={wxCode}
options={{
selectOnLineNumbers: true,
automaticLayout: true,
fontSize: `32px`
}}
theme='dark'
editorDidMount={handleEditorChange}
/>
</div>
<div className="wx-code-test">
<div className="flex-btn">
<span className="icon-btn" onClick={handleResetCode}>
<i className="iconfont icon-reset icon"></i>
<span className="icon-txt">初始化</span>
</span>
<span className="icon-btn" onClick={handleClickTestCase}>
<i className="iconfont icon-base icon"></i>
<span className="icon-txt">测试集</span>
</span>
</div>
{/* <Button type="primary" shape="circle">评测</Button> */}
<button className="wx-pt-btn" onClick={handleEvalateCode}>评测</button>
</div>
</div>
{/* 测试集 */}
<div className={tcclasses}>
<div className="text-case-list">
<div className="list-header">
<span className="header-title">{testCase.length}个测试用例</span>
<span className="header-close" onClick={handleCloseTestCase}>关闭</span>
</div>
<div className="wxcode-test-result" style={{ display: last_compile_output ? 'block' : 'none'}}>
<i className={iclasses}></i>
<span className={tclasses}>{test_sets_count - sets_error_count}/{test_sets_count}</span>
<span className={`${tclasses} result-txt-desc`}>{resultTxt}</span>
</div>
<ul className={ulClasses}>
{
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 (
<li className="case-item" key={`item_${i}`}>
<div className="case-item-header" onClick={() => handleCloseItem(i, is_public)}>
<h2 className={headerClasses}>
<i className={iconclasses}></i>
测试集{i + 1}
</h2>
{
is_public
? (result
? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>)
: (<span className="case-item-tips">
隐藏测试集暂不支持解锁和查看
{/* {result
? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>
} */}
</span>)
}
</div>
<div className={_classes}>
<span className="desc-title">测试输入</span>
<span className="test-input">{input || '-'}</span>
<span className="desc-title">预期输出</span>
{/* <textarea rows="5">预期输出</textarea> */}
<TextArea
readOnly={true}
className="text-area-style"
value={output}
onChange={this.onChange}
placeholder="Controlled autosize"
autoSize={{ minRows: 3, maxRows: 6 }}
/>
{/* <TextArea rows={5} className="hope-result">预期输出</TextArea> */}
<span className="desc-title">实际输出</span>
<TextArea
readOnly={true}
className="text-area-style"
value={actual_output}
autoSize={{ minRows: 1, maxRows: 3 }}
/>
</div>
</li>
)
})
}
</ul>
</div>
</div>
{/* 测评中 */}
<div className={loading}>
<span className="loading-flex">
<Icon className="loading-icon" type="loading" />
<span className="loading-txt">测评中...</span>
</span>
</div>
</div>
);
}
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);

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

@ -92,7 +92,14 @@ const types = {
/** 统计 */ /** 统计 */
GET_STATIC_INFO: 'GET_STATIC_INFO', GET_STATIC_INFO: 'GET_STATIC_INFO',
CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS', 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; export default types;

@ -12,6 +12,7 @@ import {
changePaginationInfo, changePaginationInfo,
deleteItem deleteItem
} from './ojList'; } from './ojList';
import { import {
validateOjForm, validateOjForm,
saveOjFormCode, saveOjFormCode,
@ -109,6 +110,16 @@ import {
initTotal initTotal
} from './static'; } from './static';
import {
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading
} from './wxCode';
export default { export default {
toggleTodo, toggleTodo,
getOJList, getOJList,
@ -191,5 +202,14 @@ export default {
// 统计 // 统计
staticList, staticList,
changeParams, changeParams,
initTotal initTotal,
// 微信
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading
} }

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

@ -17,6 +17,7 @@ import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer'; import commentReducer from './commentReducer';
import tpiReducer from './tpiReducer'; import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer'; import staticReducer from './staticReducer';
import wxcodeReducer from './wxcodeReducer';
export default combineReducers({ export default combineReducers({
testReducer, testReducer,
@ -28,5 +29,6 @@ export default combineReducers({
jupyterReducer, jupyterReducer,
commentReducer, commentReducer,
tpiReducer, tpiReducer,
staticReducer staticReducer,
wxcodeReducer
}); });

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

@ -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});
}
Loading…
Cancel
Save