forgeplus-react/src/modules/wxcode/index.js

382 lines
13 KiB
JavaScript
Raw Normal View History

2020-03-16 14:12:11 +08:00
/*
* @Description: 微信端代码编辑器
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 09:56:34
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-18 15:07:09
*/
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,
showDialog,
gold,
experience,
next_game,
// userCode,
testCase = [],
getWXCode,
last_compile_output,
test_sets_count,
sets_error_count,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading,
changeWXCodeEvaluateDialog
} = props;
const {identifier} = props.match.params;
// 获取路径参数
const setCookier = () => {
// const _params = window.location.search;
// if (_params) {
// let _search = _params.split('?')[1];
// _search.split('&').forEach(item => {
// // console.log(item);
// const _arr = item.split('=');
// cookie.remove(_arr[0], {
// path: '/',
// domain: '.educoder.net'
// });
// cookie.save(_arr[0], _arr[1], {
// path: '/',
// domain: '.educoder.net'
// });
// });
// }
}
setCookier();
const [isActive, setIsActive] = useState(-1);
const [tip, setTip] = useState('');
const [codes, setCodes] = useState(wxCode);
// const [showInfo, setShowInfo] = useState(false);
// const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (codes !== props.wxCode) {
setCodes(props.wxCode);
}
}, [props]);
const editorRef = useRef(null);
let timer = null;
const loadResult = (identifier) => {
// 加载代码块内容
getWXCode(identifier);
// 加载测试集
const params = {
path,
status: 0,
retry: 1
};
getWXCodeTestCase(identifier, params);
}
useEffect(() => {
setTimeout(() => {
setCookier();
loadResult(identifier);
}, 0);
}, []);
// 关闭
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);
}, 5000);
}
}
// 关闭单个测试集
const handleCloseItem = (i, flag) => {
if (!flag) return;
setIsActive(isActive === i ? -1 : i);
}
// 初始化
const handleResetCode = () => {
clearInterval(timer);
timer = null;
const result = window.confirm('你在本文件中修改的内容将丢失, 是否确定重新加载初始代码?');
if (result) {
setTip('初始化中...');
changeWXCodeEvaluateLoading(true);
identifier && restoreWXCode(identifier, { path });
}
}
// 评测
const handleEvalateCode = () => {
changeWXCodeEvaluateLoading(true);
evaluateWxCode(identifier, path);
setTip('评测中...');
}
// 关闭弹框
const handleCloseDialog = () => {
changeWXCodeEvaluateDialog(false);
}
// 查看测试集
const handleClickShowResult = () => {
showWXCodeTextCase(true);
changeWXCodeEvaluateDialog(false);
}
// 下一关
const handleClickNext = () => {
changeWXCodeEvaluateDialog(false);
loadResult(next_game);
}
const tcclasses = isShow ? `wx-code-test-case active` : 'wx-code-test-case';
const loading = showLoading ? 'code-evaluate-loading active' : 'code-evaluate-loading';
const dialog = showDialog ? 'pass-dialog active' : 'pass-dialog';
const _val = +sets_error_count === 0;
let resultTxt = (_val) ? '全部通过' : `${sets_error_count}组测试结果不匹配`;
const iclasses = _val ? 'iconfont icon-wancheng 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';
const resultFlag = !!last_compile_output ? 'wxcode-test-result active' : 'wxcode-test-result';
// console.log('==========>>>>>>>> 评测结果样式', last_compile_output, resultFlag);
return (
<div className="wx-code-area">
<div className="wx-code-flex">
<div className="wx-code-item">
<MonacoEditor
height="100%"
width="100%"
language="python"
value={codes}
options={{
selectOnLineNumbers: true,
automaticLayout: true,
fontSize: `42px`
}}
theme='dark'
editorDidMount={handleEditorChange}
/>
</div>
<div className="wx-code-test">
<div className="flex-btn">
<span className="icon-btn" onClick={handleResetCode}>
<i className="iconfont icon-chushihua icon"></i>
<span className="icon-txt">初始化</span>
</span>
<span className="icon-btn" onClick={handleClickTestCase}>
<i className="iconfont icon-ceshiji 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={resultFlag}>
<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, compile_success} = 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
? compile_success > 0
? result ? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>
: ''
:
<span className="case-item-tips">隐藏测试集暂不支持解锁和查看 </span>
// is_public
// ? result ? <span className="iconfont icon-wancheng case_item_success"></span>
// : <span className="iconfont icon-jinggao1 case_item_fail"></span>)
// : (
// 隐藏测试集,暂不支持解锁和查看
// {/* {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">{tip}</span>
</span>
</div>
{/* 通过弹框 */}
<div className={dialog}>
<div className="pass-box">
<div className="pass-img"></div>
<div className="pass-ctx">
<div className="pass-title">{next_game ? '评测通过' : '恭喜通关'}</div>
<p className="pass-value">
金币 <span className="value_color">{gold > 0 ? `+${gold}` : 0}</span>,
经验值 <span className="value_color">{experience > 0 ? `+${experience}` : 0}</span>
</p>
{
next_game
? (
<div className="pass-btn">
<button className="btn btn-first" onClick={handleClickShowResult}>评测结果</button>
<button className="btn btn-second" onClick={handleClickNext}>下一关</button>
</div>
)
: (
<div className="pass-btn-all">
<button className="btn btn-second" onClick={handleClickShowResult}>评测结果</button>
</div>
)
}
</div>
</div>
<i className="iconfont icon-roundclose icon_close" onClick={handleCloseDialog}></i>
</div>
</div>
);
}
const mapStateToProps = (state) => {
const {
path,
isShow,
wxCode,
userCode,
gold,
experience,
next_game,
testCase,
showLoading,
showDialog,
last_compile_output,
test_sets_count,
sets_error_count
} = state.wxcodeReducer;
// console.log(state);
return {
path,
isShow,
wxCode,
userCode,
gold,
experience,
next_game,
testCase,
showLoading,
showDialog,
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)),
changeWXCodeEvaluateDialog: (flag) => dispatch(actions.changeWXCodeEvaluateDialog(flag))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);