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

dev_static
daiao 5 years ago
commit d72ccd63bc

@ -83,7 +83,7 @@ export function initAxiosInterceptors(props) {
//proxy="http://47.96.87.25:48080"
proxy="https://pre-newweb.educoder.net"
proxy="https://test-newweb.educoder.net"
// proxy="https://test-jupyterweb.educoder.net"
proxy="https://test-jupyterweb.educoder.net"
//proxy="http://192.168.2.63:3001"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
@ -123,7 +123,10 @@ export function initAxiosInterceptors(props) {
}
}
if(`${config[0]}`!=`true`){
let timestamp = Date.parse(new Date())/1000;
if (window.location.port === "3007") {
// let timestamp=railsgettimes(proxy);
console.log(timestamp)
railsgettimes(`${proxy}/api/main/first_stamp.json`);
let newopens=md5(opens+timestamp)
config.url = `${proxy}${url}`;

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-12-16 15:50:45
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 15:07:11
* @LastEditTime : 2019-12-31 13:59:02
*/
import Quill from "quill";
@ -50,6 +50,7 @@ export default class ImageBlot extends BlockEmbed {
return node;
}
// 获取节点值
static value (node) {
return {

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-12-18 08:49:30
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 16:49:25
* @LastEditTime : 2019-12-31 15:11:37
*/
import './index.scss';
import 'quill/dist/quill.core.css'; // 核心样式
@ -18,10 +18,18 @@ import deepEqual from './deepEqual.js'
import { fetchUploadImage } from '../../services/ojService.js';
import { getImageUrl } from 'educoder'
import ImageBlot from './ImageBlot';
const Size = Quill.import('attributors/style/size');
const Font = Quill.import('formats/font');
// const Color = Quill.import('attributes/style/color');
Size.whitelist = ['12px', '14px', '16px', '18px', '20px', false];
Font.whitelist = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif'];
window.Quill = Quill;
window.katex = katex;
Quill.register(ImageBlot);
Quill.register(Size);
Quill.register(Font, true);
// Quill.register(Color);
function QuillForEditor ({
placeholder,
@ -38,15 +46,16 @@ function QuillForEditor ({
}) {
// toolbar 默认值
const defaultConfig = [
['bold', 'italic', 'underline'],
[{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表
[{script: 'sub'}, {script: 'super'}],
[{ 'color': [] }, { 'background': [] }],
[{header: [1,2,3,4,5,false]}],
['blockquote', 'code-block'],
['link', 'image', 'video'],
['formula'],
['clean']
'bold', 'italic', 'underline',
{size: ['12px', '14px', '16px', '18px', '20px']},
{align: []}, {list: 'ordered'}, {list: 'bullet'}, // 列表
{script: 'sub'}, {script: 'super'},
{ 'color': [] }, { 'background': [] },
{header: [1,2,3,4,5,false]},
'blockquote', 'code-block',
'link', 'image', 'video',
'formula',
'clean'
];
const editorRef = useRef(null);

@ -7,4 +7,107 @@
cursor: pointer;
}
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
content: '12px';
font-size: 12px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
content: '14px';
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
content: '16px';
font-size: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
content: '18px';
font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
content: '20px';
font-size: 20px;
}
//
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: '14px';
font-size: 14px;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
content: "宋体";
font-family: "SimSun";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
content: "黑体";
font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
content: "微软雅黑";
font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
content: "楷体";
font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
content: "仿宋";
font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
content: "Arial";
font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
content: "Times New Roman";
font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
content: "sans-serif";
font-family: "sans-serif";
}
.ql-font-SimSun {
font-family: "SimSun";
}
.ql-font-SimHei {
font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
font-family: "KaiTi";
}
.ql-font-FangSong {
font-family: "FangSong";
}
.ql-font-Arial {
font-family: "Arial";
}
.ql-font-Times-New-Roman {
font-family: "Times New Roman";
}
.ql-font-sans-serif {
font-family: "sans-serif";
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "微软雅黑";
font-family: "Microsoft YaHei";
}
}

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-20 23:10:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 15:53:27
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-02 14:57:02
*/
const CONST = {
jcLabel: {
@ -12,9 +12,11 @@ const CONST = {
language: '编程语言',
description: '描述',
difficult: '难易度',
category: '分类',
category: '课程',
openOrNot: '公开程序',
timeLimit: '时间限制'
timeLimit: '时间限制',
knowledge: '知识点',
sub_discipline_id: '课程'
},
fontSetting: {
title: '代码格式',

@ -253,22 +253,22 @@ class Fileslistitem extends Component{
{discussMessage.is_publish===false?<CoursesListType typelist={["未发布"]} typesylename={""}/>:""}
{this.props.isAdmin?
<span className={"fr mr10 mt2"} onClick={(event)=>this.eventStop(event)}>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<span className={"fr mt2"} onClick={(event)=>this.eventStop(event)}>
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
</span>:""}
{this.props.isStudent===true&&this.props.current_user.login===discussMessage.author.login?
<span className={"fr mr10 mt2"} onClick={(event)=>this.eventStop(event)}>
<span className={"fr mt2"} onClick={(event)=>this.eventStop(event)}>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.onDelete(discussMessage.id)}>删除</a>
</WordsBtn>
@ -329,7 +329,7 @@ class Fileslistitem extends Component{
{ discussMessage.publish_time===null?"":discussMessage.is_publish===true?moment(discussMessage.publish_time).fromNow():moment(discussMessage.publish_time).format('YYYY-MM-DD HH:mm')}
</span>
</span>
{discussMessage&&discussMessage.category_name===null?"":this.props.child===false?<div className="color-grey9 task-hide fr mr60" title={discussMessage&&discussMessage.category_name}
{discussMessage&&discussMessage.category_name===null?"":this.props.child===false?<div className="color-grey9 task-hide fr mr30" title={discussMessage&&discussMessage.category_name}
style={{"max-width":"268px"}}>所属目录{discussMessage&&discussMessage.category_name}
</div>:""}
</p>

@ -370,11 +370,11 @@ class ShixunWorkReport extends Component {
{this.props.isAdmin() ?<a
className=" color-blue font-14 fr ml20 mt15"
onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)}
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告数据</Spin></a>:
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告</Spin></a>:
parseInt(this.props&&this.props.user.user_id)===parseInt(data&&data.user_id)?<a
className=" color-blue font-14 fr ml20 mt15"
onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)}
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告数据</Spin></a>:""
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告</Spin></a>:""
}
{/*{this.props.isAdmin() ?work_comment_hidden===true? "":<a*/}
{/*className=" color-blue font-14 fr ml20 mt15"*/}
@ -465,22 +465,22 @@ class ShixunWorkReport extends Component {
</div>
<div className={"fl edu-back-white ml39 "}>
<p className={"back_font"}>{data&&data.username}</p>
<p className={"back_font mt10"}>{data&&data.username}</p>
<p className={"mb16"}>
<span className={"passbox"}>
<div className={"passfont"}><span className={"color999"}>通过关卡</span> <span className={"colorCF3B3B"}>{data&&data.complete_count}/{data&&data.challenges_count}</span></div>
<p className={"mb16 mt10"}>
<span className={"passbox "}>
<div className={"passfont mb5"}><span className={"color999"}>当前完成关卡</span> <span className={"colorCF3B3B"}>{data&&data.complete_count}/{data&&data.challenges_count}</span></div>
<div className={"passfontbom"}><span className={"color999"}>经验值</span> <span className={"color333"}>{data&&data.myself_experience}/{data&&data.total_experience}</span></div>
</span>
<span className={"passbox"}>
<div className={"passfontmid"}><span className={"color999"}>课堂最高完成效率</span> <span className={data&&data.max_efficiency===null?"color999":"color333"}>{data&&data.max_efficiency===null?'--':data&&data.max_efficiency}</span></div>
<div className={"passfontbommid"}><span className={"color999"}>完成效率</span> <span className={data&&data.efficiency===null?"color999":"color333"}>{data&&data.efficiency===null?'--':data&&data.efficiency}</span></div>
<div className={"passfontbommid mb5"}><span className={"color999"}>完成效率</span> <span className={data&&data.efficiency===null?"color999":"color333"}>{data&&data.efficiency===null?'--':data&&data.efficiency}</span></div>
<div className={"passfontmid "}><span className={"color999"}>课堂最高完成效率</span> <span className={data&&data.max_efficiency===null?"color999":"color333"}>{data&&data.max_efficiency===null?'--':data&&data.max_efficiency}</span></div>
</span>
<span className={"passbox"}>
<div><span className={"color999"}>通关时间</span> <span className={data&&data.passed_time===null?"color999":"color333"}>{data&&data.passed_time===null||data&&data.passed_time=== "--"?'--':moment(data&&data.passed_time).format('YYYY-MM-DD HH:mm')}</span></div>
<div><span className={"color999"}>实战耗时</span> <span className={data&&data.efficiency===null?"color999":"color333"}>{data&&data.time_consuming===null?'--':data&&data.time_consuming}</span></div>
{/*<div><span className={"color999"}>实战耗时:</span> <span className={data&&data.efficiency===null?"color999":"color333"}>{data&&data.time_consuming===null?'--':data&&data.time_consuming}</span></div>*/}
</span>
</p>

@ -2678,7 +2678,7 @@ class Trainingjobsetting extends Component {
onChange={this.onChangeeffectiveness}
checked={this.state.work_efficiencys} style={{color: "#666666"}}>效率分<span
className={"font-14 color-grey-c font-16 ml15"}
style={{textAlign: "left"}}>(选中则学生最终成绩包含效率分)</span>
style={{textAlign: "left"}}>(选中则学生最终成绩包含效率分效率分在作业截止或者补交结束后由系统自动生成)</span>
</Checkbox>
<div>

@ -66,7 +66,7 @@ class ConclusionEvaluation extends Component {
</span>
),
},{
title: '最终成绩',
title: '当前成绩',
dataIndex: 'grade',
key: 'grade',
render: (text, record) => (
@ -81,7 +81,7 @@ class ConclusionEvaluation extends Component {
</span>
),
}, {
title: '实战耗时',
title: '实战耗时',
key: 'elapsed',
dataIndex: 'elapsed',

@ -0,0 +1,127 @@
/*
* @Description: 知识点
* @Author: tangjiang
* @Github:
* @Date: 2019-12-30 13:51:19
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-03 09:32:24
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
import { Select, notification } from 'antd';
const { Option } = Select;
function KnowLedge (props) {
const {
options = [], // 下拉选项
values = [], // 已选择的下拉项
onChange // 获取选择的值
} = props;
useEffect(() => {
const _options = [];
const _selects = [];
options.forEach(opt => {
if (!values.includes(opt.id)) {
_options.push(opt);
} else {
_selects.push(opt);
}
});
setSelectOptions(_options || []);
setSelectValue(_selects || []);
}, [props]);
// 显示的下拉项
const [selectOptions, setSelectOptions] = useState(options);
// 已选择的下拉项
const [selectValue, setSelectValue] = useState([]);
//
const [value] = useState([]);
// 渲染下拉选项
const renderOptions = (options = []) => {
return options.map((opt, i) => (
<Option key={`opt_${i}`} value={`${opt.id}`}>{opt.name}</Option>
));
}
// 过滤下拉列表项
const handleSelectChange = (value) => {
value = +value.join('');
const tempArr = [...selectValue];
const _result = selectOptions.filter(item => {
if (item.id === value && tempArr.findIndex(t => t.id === value) === -1) {
tempArr.push(item);
}
return item.id !== value;
});
if (tempArr.length > 50) {
notification.warning({
message: '提示',
description: '知识点不能超过50个'
});
return;
}
setSelectValue(tempArr);
setSelectOptions(_result);
// 将选择值返回
onChange && onChange(tempArr);
}
// 删除
const handleRemoveResult = (item) => {
// console.log('点击了删除按钮===>>>>', item);
// 将删除的值重新加入到列表中
const tempOptions = [...selectOptions];
const tempValue = selectValue.filter(t => t.id !== item.id);
// console.log(selectValue);
tempOptions.push(item);
setSelectOptions(tempOptions);
setSelectValue(tempValue);
// 将选择值返回
onChange && onChange(tempValue);
}
// 渲染下拉结果
const renderResult = (arrs) => {
return arrs.map((item) => (
<span className="knowledge-item" key={`item_${item.name}`}>
{item.name}
<span
onClick={() => handleRemoveResult(item)}
className="iconfont icon-roundclose knowledge-close"
></span>
</span>
));
}
// 渲染下拉列表
const renderSelect = (options = []) => {
// console.log('+++++', options);
// setSelectValue(_selects);
return (
<Select
value={value}
mode="tags"
placeholder="请选择"
style={{ width: '100%' }}
onChange={handleSelectChange}
>
{renderOptions(options)}
</Select>
)
}
return (
<div className="knowledge-select-area">
{ renderSelect(selectOptions) }
{/* 渲染下拉选择项 */}
<div className="knowledge-result">
{ renderResult(selectValue) }
</div>
</div>
);
}
export default KnowLedge;

@ -0,0 +1,42 @@
.knowledge-select-area{
.ant-select-selection__rendered{
margin-bottom: 0 !important;
}
.ant-select-search--inline{
margin-left: 5px;
margin-top: 2px;
}
.knowledge-result{
display: flex;
flex-direction: row;
flex-wrap: wrap;
// margin-top: 15px;
.knowledge-item{
position: relative;
border: 1px solid #DDDDDD;
border-radius: 3px;
padding: 10px;
background: #fff;
margin-right: 10px;
margin-top: 10px;
// margin-bottom: 10px;
.knowledge-close{
display: none;
position: absolute;
right: -10px;
top: -10px;
background-color: rgba(250,250,250,1);
cursor: pointer;
}
&:hover{
.knowledge-close{
display: block;
}
}
}
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 15:02:52
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 20:49:46
* @LastEditTime : 2020-01-02 13:59:38
*/
import './index.scss';
import React, { useState, useRef, useEffect } from 'react';

@ -35,6 +35,7 @@ const NewOrEditTask = (props) => {
getUserInfoForNew,
handleCancelPublish,
validateOjForm,
getQuestion
// updateTestAndValidate,
} = props;
@ -53,6 +54,10 @@ const NewOrEditTask = (props) => {
useEffect(() => {
// 获取用户信息
getUserInfoForNew();
// 获取课程列表
getQuestion({
source: 'question'
});
// console.log('获取路由参数: ====', props.match.params);
const id = props.match.params.id;
// 保存OJForm的id号指明是编辑还是新增
@ -273,7 +278,8 @@ const mapDispatchToProps = (dispatch) => ({
startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)),
// 新建时获取信息
getUserInfoForNew: () => dispatch(actions.getUserInfoForNew()),
validateOjForm: (props, type, cb) => dispatch(actions.validateOjForm(props, type, cb))
validateOjForm: (props, type, cb) => dispatch(actions.validateOjForm(props, type, cb)),
getQuestion: (params) => dispatch(actions.getQuestion(params))
});
export default withRouter(connect(

@ -4,12 +4,12 @@
* @Github:
* @Date: 2019-11-20 10:35:40
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 14:30:55
* @LastEditTime : 2020-01-03 09:20:12
*/
import './index.scss';
// import 'katex/dist/katex.css';
import React from 'react';
import { Form, Input, Select, InputNumber, Button } from 'antd';
import { Form, Input, Select, InputNumber, Button, Cascader, notification } from 'antd';
import { connect } from 'react-redux';
import AddTestDemo from './AddTestDemo';
// import QuillEditor from '../../../quillEditor';
@ -18,6 +18,7 @@ import CONST from '../../../../../constants';
import { toStore } from 'educoder'; // 保存和读取store值
// import Wrapper from '../../../../../common/reactQuill';
import QuillForEditor from '../../../../../common/quillForEditor';
import KnowLedge from '../../../components/knowledge';
const scrollIntoView = require('scroll-into-view');
const {jcLabel} = CONST;
const FormItem = Form.Item;
@ -59,7 +60,9 @@ class EditTab extends React.Component {
scrollHeight: 0, // 滚动元素的高度
top: 500,
bottom: 20,
offsetTop: 0
offsetTop: 0,
// knowledges: [],
// coursers: [] // 选中的课程
}
}
@ -77,9 +80,17 @@ class EditTab extends React.Component {
}, () => {
this.state.scrollEl.addEventListener('scroll', this.handleScroll, false);
});
// 获取题库
// this.props.getQuestion({
// source: 'question'
// });
}
componentWillUnmount () {
// componentDidUpdate (nextProp) {
// console.log(nextProp);
// }
componentWillUnmount (nextPro) {
this.state.scrollEl.removeEventListener('scroll', this.handleScroll, false);
}
@ -125,9 +136,26 @@ class EditTab extends React.Component {
handleTimeLimitChange = (value) => {
this.props.validateOjTimeLimit(value);
}
// 改变分类
handleChangeCategory = (value) => {
this.props.validateOjCategory(value);
// 改变方向
handleChangeSubDisciplineId = (value) => {
// 课程下拉值变化时, 同步更新知识点
const { courseQuestions, saveKnowledge } = this.props;
saveKnowledge([]);
// 获取当前分类下的知识点
courseQuestions.forEach(item => {
if (value[0] && item.id === value[0]) {
item.sub_disciplines && item.sub_disciplines.forEach(c => {
if (value[1] && c.id === value[1]) {
saveKnowledge(c.tag_disciplines)
console.log(c.tag_disciplines);
} else if (!value[1]) {
saveKnowledge([]);
}
});
}
});
// this.props.validateOjCategory(value[1] || '');
this.props.validateOjSubDisciplineId(value[1] || '');
}
// 改变公开程序
handleChangeOpenOrNot = (value) => {
@ -146,13 +174,18 @@ class EditTab extends React.Component {
addTestCase, // 添加测试用例
deleteTestCase, // 删除测试用例
testCasesValidate,
openTestCodeIndex = []
openTestCodeIndex = [],
courseQuestions,
tag_discipline_id,
knowledges
} = this.props;
console.log('knowledge======>>>>>>', knowledges);
// const {knowledges} = this.state;
// 表单label
const myLabel = (name, subTitle) => {
const myLabel = (name, subTitle, nostar) => {
if (subTitle) {
return (
<span className={'label_text'}>
<span className={`label_text ${nostar}`}>
{name}
<span className={'label_sub_text'}>
({subTitle})
@ -161,7 +194,7 @@ class EditTab extends React.Component {
)
} else {
return (
<span className={'label_text'}>{name}</span>
<span className={`label_text ${nostar}`}>{name}</span>
)
}
};
@ -197,7 +230,14 @@ class EditTab extends React.Component {
};
// 添加测试用例
const handleAddTest = () => {
const {position} = this.props;
const {position, testCases = []} = this.props;
if (testCases.length >= 50) {
notification.warning({
message: '提示',
description: '测试用例不能超过50个'
});
return;
}
const obj = { // 测试用例参数
input: '',
output: '',
@ -235,15 +275,74 @@ class EditTab extends React.Component {
}
// 编辑器配置信息
const quillConfig = [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline', 'strike'], // 切换按钮
['blockquote', 'code-block'], // 代码块
[{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }], // 列表
[{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'color': [] }, { 'background': [] }], // 字体颜色与背景色
['image', 'formula'], // 数学公式、图片、视频
['clean'], // 清除格式
{ header: 1}, {header: 2},
// {size: ['12px', '14px', '16px', '18px', '20px', false]},
'bold', 'italic', 'underline', 'strike', // 切换按钮
'blockquote', 'code-block', // 代码块
{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表
{ 'script': 'sub'}, { 'script': 'super' },
{ 'color': [] }, { 'background': [] }, // 字体颜色与背景色
// {font: []},
'image', 'formula', // 数学公式、图片、视频
'clean', // 清除格式
];
const renderCourseQuestion = (arrs) => {
const tempArr = [];
const sub_id = this.props.ojForm.sub_discipline_id;
function loop (arrs, tempArr) {
arrs.forEach(item => {
const obj = {};
obj.value = item.id;
obj.label = item.name;
// 当item下还有子元素时递归调用
if (item.sub_disciplines) {
arrs = item.sub_disciplines;
obj.children = [];
loop(arrs, obj.children);
}
tempArr.push(obj);
});
}
loop(arrs, tempArr);
// 获取选中的下拉值
let choid_ids = [];
// let tempKnowledges = [];
tempArr.forEach(t => {
// debugger;
if (sub_id && t.children) {
t.children.forEach(c => {
if (c.value === sub_id) {
choid_ids = [t.value, c.value];
// tempKnowledges = c.children || [];
}
});
}
});
console.log(choid_ids);
return (
<Cascader
options={tempArr}
expandTrigger="hover"
value={choid_ids}
// onChange={this.handleChangeCategory}
onChange={this.handleChangeSubDisciplineId}
/>
)
}
// 知识点
const handleKnowledgeChange = (values= []) => {
const _result = [];
values.forEach(v => {
_result.push(v.id);
});
console.log('下拉选择的值:===>>>', _result);
// 保存选择的知识点
this.props.saveTagDisciplineId(_result);
}
return (
<div className={'editor_area'} id="textCase">
@ -262,16 +361,34 @@ class EditTab extends React.Component {
<FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['category'], '合理的分类有利于快速检索')}</span>}
validateStatus={ojFormValidate.category.validateStatus}
help={ojFormValidate.category.errMsg}
label={<span>{myLabel(jcLabel['sub_discipline_id'], '合理的课程分类有利于快速检索')}</span>}
validateStatus={ojFormValidate.sub_discipline_id.validateStatus}
help={ojFormValidate.sub_discipline_id.errMsg}
colon={ false }
>
<Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
{/* <Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
{getOptions('category')}
</Select>
</Select> */}
{/* <Cascader
options={courseQuestions}
expandTrigger="hover"
onChange={this.handleChangeCategory}
/> */}
{ renderCourseQuestion(courseQuestions)}
</FormItem>
<FormItem
colon={ false }
className='input_area flex_100'
label={<span>{myLabel(jcLabel['knowledge'], '', 'nostar')}</span>}
>
<KnowLedge
options={knowledges}
values={tag_discipline_id}
onChange={handleKnowledgeChange}
/>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
@ -317,16 +434,13 @@ class EditTab extends React.Component {
help={ojFormValidate.description.errMsg}
colon={ false }
>
<div style={{ marginTop: '15px'}}>
<QuillForEditor
<QuillForEditor
style={{ height: '200px' }}
placeholder="请输入描述信息"
onContentChange={handleContentChange}
options={quillConfig}
value={ojForm.description}
/>
</div>
</FormItem>
{/* <FormItem
@ -366,14 +480,21 @@ const mapStateToProps = (state) => {
testCases,
openTestCodeIndex,
testCasesValidate,
ojFormValidate} = ojFormReducer;
ojFormValidate,
courseQuestions,
tag_discipline_id,
knowledges
} = ojFormReducer;
return {
ojForm,
testCases,
testCasesValidate,
ojFormValidate,
position,
openTestCodeIndex
openTestCodeIndex,
courseQuestions,
tag_discipline_id,
knowledges
};
};
@ -386,10 +507,15 @@ const mapDispatchToProps = (dispatch) => ({
validateOjTimeLimit: (value) => dispatch(actions.validateOjTimeLimit(value)),
validateOjCategory: (value) => dispatch(actions.validateOjCategory(value)),
validateOpenOrNot: (value) => dispatch(actions.validateOpenOrNot(value)),
validateOjSubDisciplineId: (value) => dispatch(actions.validateOjSubDisciplineId(value)),
saveTagDisciplineId: (value) => dispatch(actions.saveTagDisciplineId(value)),
// 新增测试用例
addTestCase: (value) => dispatch(actions.addTestCase(value)),
// 删除测试用例
deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)),
saveKnowledge: (value) => dispatch(actions.saveKnowledge(value))
// 获取题库
// getQuestion: (params) => dispatch(actions.getQuestion(params))
});
export default connect(

@ -18,6 +18,12 @@
line-height: 1;
content: '*';
}
&.nostar{
&::before {
content: ''
}
}
}
.input_area{
display: inline-block;

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-12-04 08:36:21
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 21:18:39
* @LastEditTime : 2020-01-02 13:48:02
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
@ -57,7 +57,9 @@ function RecordDetail (props) {
const handleReturn = (identifier) => {
if (identifier) {
saveEditorCodeForDetail('');
props.history.push(`/myproblems/${identifier}`);
setTimeout(() => {
props.history.push(`/myproblems/${identifier}`);
}, 300);
}
}

@ -23,6 +23,8 @@
}
.header_title{
font-size: 16px;
font-weight: bold;
text-align: center;
}
@ -82,6 +84,8 @@
bottom: 0;
top: 0;
text-align: center;
font-size: 16px;
font-weight: bold;
}
}
}
@ -158,6 +162,22 @@
-moz-background-clip: padding;
-webkit-background-clip: padding;
background-clip: padding-box;
// &::before{
// position: absolute;
// width: 24px;
// height: 24px;
// border-radius: 50%;
// margin-top: -12px;
// top: 50%;
// right: -12px;
// font-family: 'iconfont';
// background: gold;
// content: '\e711';
// font-size: 18px;
// text-align: center;
// line-height: 24px;
// }
}
.Resizer:hover {
@ -196,4 +216,4 @@
}
.Resizer.disabled:hover {
border-color: transparent;
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-23 11:33:41
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 16:03:04
* @LastEditTime : 2020-01-02 13:51:22
// */
import './index.scss';
import React, { useState, useEffect, useMemo } from 'react';
@ -50,8 +50,6 @@ const LeftPane = (props) => {
record: (<CommitRecord />),
comment: (<Comment />)
};
console.log('======>>>>>>>', props);
useEffect(() => {
setDefaultActiveKey(userCodeTab);

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 14:59:51
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 19:23:46
* @LastEditTime : 2020-01-02 14:23:43
*/
import React, { useState, useEffect } from 'react';
import {connect} from 'react-redux';
@ -39,7 +39,7 @@ const RightPane = (props) => {
changeLoadingState
} = props;
const [editorCode, setEditorCode] = useState(editor_code || hack.code);
// const [editorCode, setEditorCode] = useState(editor_code || hack.code);
const [noteClazz, setNoteClazz] = useState('editor_nodte_area');
const [noteCount] = useState(5000);
// const [code, setCode] = useState(editor_code || hack.code);
@ -65,8 +65,9 @@ const RightPane = (props) => {
let timer = null; // 定时器
// 代码块内容变化时
const handleCodeChange = (value) => {
// console.log('编辑器代码 ======》》》》》》》》》++++++++++', value);
saveUserInputCode(value);
setEditorCode(value);
// setEditorCode(value);
if (!timer) {
timer = setInterval(function () {
clearInterval(timer);
@ -116,6 +117,7 @@ const RightPane = (props) => {
const { getFieldDecorator } = props.form;
return (
<div className={'right_pane_code_wrap'}>
<MyMonacoEditor
notice={notice}
identifier={identifier}

@ -160,7 +160,7 @@
.newyslusercjz{
display: inline-block;
position: absolute;
bottom: 0px;
bottom: 10px;
left: -18px;
width: 44px;
height: 18px;

@ -18,7 +18,9 @@ const types = {
VALIDATE_OJ_DIFFICULT: 'VALIDATE_OJ_DIFFICULT', // 难易度
VALIDATE_OJ_TIMELIMIT: 'VALIDATE_OJ_TIMELIMIT', // 时间限制
VALIDATE_OJ_CATEGORY: 'VALIDATE_OJ_CATEGORY', // 分类
VALIDATE_OJ_SUB_DISCIPLINE_ID: 'VALIDATE_OJ_SUB_DISCIPLINE_ID', // 方向
VALIDATE_OJ_OPENORNOT: 'VALIDATE_OJ_OPENORNOT', // 公开程序
SAVE_TAG_DISCIPLINE_ID: 'SAVE_TAG_DISCIPLINE_ID', // 保存知识点
SAVE_OJ_FORM: 'SAVE_OJ_FORM', // 保存表单
ADD_TEST_CASE: 'ADD_TEST_CASE', // 添加测试用例
DELETE_TEST_CASE: 'DELETE_TEST_CASE', // 删除测试用例
@ -32,6 +34,9 @@ const types = {
TEST_CASE_INPUT_CHANGE: 'TEST_CASE_INPUT_CHANGE', // 测试用例输入值改变时
TEST_CASE_OUTPUT_CHANGE: 'TEST_CASE_OUTPUT_CHANGE', // 测试用例输出值改变时
DEBUGGER_CODE: 'DEBUGGER_CODE', // 调试代码
GET_COURSE_QUESTION: 'GET_COURSE_QUESTION', // 获取编辑题
CHANGE_KNOWLEDGES: 'CHANGE_KNOWLEDGES', // 保存所选择的知识点
// study
SAVE_USER_PROGRAM_ID: 'SAVE_USER_PROGRAM_ID',// 保存用户编程题id值
USER_PROGRAM_DETAIL: 'USER_PROGRAM_DETAIL', // 用户编程题详情
SHOW_OR_HIDE_CONTROL: 'SHOW_OR_HIDE_CONTROL', // 显示或隐藏控制台

@ -25,6 +25,8 @@ import {
validateOjTimeLimit,
validateOjCategory,
validateOpenOrNot,
validateOjSubDisciplineId,
saveTagDisciplineId,
addTestCase,
deleteTestCase,
testCaseInputChange,
@ -32,6 +34,8 @@ import {
updateTestAndValidate,
updateOpenTestCaseIndex,
handleClickCancelPublish,
getQuestion,
saveKnowledge
} from './ojForm';
import {
@ -109,7 +113,12 @@ export default {
validateOjTimeLimit,
validateOjCategory,
validateOpenOrNot,
validateOjSubDisciplineId,
saveTagDisciplineId,
handleClickCancelPublish,
getQuestion,
saveKnowledge,
//
addTestCase,
deleteTestCase,
testCaseInputChange,

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:42:11
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 11:06:09
* @LastEditTime : 2020-01-02 14:17:49
*/
import types from "./actionTypes";
import { Base64 } from 'js-base64';
@ -116,16 +116,15 @@ export const saveUserCodeForInterval = (identifier, code) => {
});
// console.log('+++', userCode);
fetchUpdateCode(identifier, {
code: userCode
code: Base64.encode(userCode)
}).then(res => {
if (res.data.status === 401) {
return;
};
dispatch({
type: types.RESTORE_INITIAL_CODE,
payload: userCode
});
// dispatch({
// type: types.RESTORE_INITIAL_CODE,
// payload: userCode
// });
setTimeout(() => {
dispatch({
type: types.AUTO_UPDATE_CODE,
@ -342,7 +341,7 @@ export const getUserCommitRecord = (identifier) => {
export const getUserCommitRecordDetail = (identifier) => {
return (dispatch) => {
fetchUserCommitRecordDetail(identifier).then(res => {
console.log('提交记录详情======》》》》', res);
// console.log('提交记录详情======》》》》', res);
const { data } = res;
if (data.status === 401) return;
dispatch({

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 16:35:46
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 22:19:15
* @LastEditTime : 2020-01-02 17:38:12
*/
import types from './actionTypes';
import CONST from '../../constants';
@ -12,7 +12,8 @@ import {
fetchPostOjForm,
fetchGetOjById,
publishTask,
cancelPublicTask
cancelPublicTask,
fetchQuestion
} from '../../services/ojService';
import { Base64 } from 'js-base64';
import { notification } from 'antd';
@ -45,6 +46,10 @@ const maps = {
label: jcLabel['category'],
type: types.VALIDATE_OJ_CATEGORY
},
sub_discipline_id: {
label: jcLabel['sub_discipline_id'],
type: types.VALIDATE_OJ_SUB_DISCIPLINE_ID
},
openOrNot: {
label: jcLabel['openOrNot'],
type: types.VALIDATE_OJ_OPENORNOT
@ -105,21 +110,24 @@ export const validateOjForm = (props, type, cb) => {
const {ojForm, testCases, identifier, code } = getState().ojFormReducer;
// console.log('code', code);
/** 表单验证开始 */
let keys = Object.keys(ojForm);
// let keys = Object.keys(ojForm).filter(k => k !== '');
let keys = Object.keys(ojForm)
// 循环判断每个字段是否为空
let hasSuccess = true;
keys.forEach(key => {
const value = ojForm[key];
const validateResult = emptyValidate(key, value);
const errMsg = validateResult[key].errMsg;
if (errMsg) {
hasSuccess = false;
dispatch(
{
type: maps[key].type,
payload: payloadInfo(key, value, errMsg, validateResult[key])
}
)
if (!['category'].includes(key)) {
const value = ojForm[key];
const validateResult = emptyValidate(key, value);
const errMsg = validateResult[key].errMsg;
if (errMsg) {
hasSuccess = false;
dispatch(
{
type: maps[key].type,
payload: payloadInfo(key, value, errMsg, validateResult[key])
}
)
}
}
});
// 验证测试用例中的数组是否都有对应的值
@ -197,8 +205,8 @@ export const validateOjForm = (props, type, cb) => {
if (hasSuccess) {
// console.log('表单保存的数据为: ', getState());
const {ojFormReducer} = getState();
const {code, score, ojForm, testCases = []} = ojFormReducer;
const {category, description, difficult, language, name, openOrNot, timeLimit} = ojForm;
const {code, score, ojForm, testCases = [], tag_discipline_id = []} = ojFormReducer;
const {category, description, difficult, language, name, openOrNot, timeLimit, sub_discipline_id} = ojForm;
let paramsObj = {};
const hack = { // 编程题干
name,
@ -207,6 +215,8 @@ export const validateOjForm = (props, type, cb) => {
category,
'open_or_not': openOrNot,
'time_limit': timeLimit,
sub_discipline_id,
// tag_discipline_id,
score
};
@ -223,7 +233,8 @@ export const validateOjForm = (props, type, cb) => {
paramsObj['params'] = {
hack,
hack_sets: tempTc,
hack_codes
hack_codes,
tags: tag_discipline_id
}
paramsObj['submitType'] = 'add';
} else { // 存在时调用更新接口
@ -242,7 +253,8 @@ export const validateOjForm = (props, type, cb) => {
hack,
hack_sets,
hack_codes,
update_hack_sets
update_hack_sets,
tags: tag_discipline_id
}
paramsObj['submitType'] = 'update';
paramsObj['identifier'] = identifier;
@ -483,6 +495,15 @@ export const validateOjCategory = (value) => {
payload: payloadInfo('category', value, errMsg, validate)
}
};
// 验证方向
export const validateOjSubDisciplineId = (value) => {
const validate = emptyValidate('sub_discipline_id', value)['sub_discipline_id'];
const errMsg = validate.errMsg;
return {
type: types.VALIDATE_OJ_SUB_DISCIPLINE_ID,
payload: payloadInfo('sub_discipline_id', value, errMsg, validate)
}
};
// 验证公开程序
export const validateOpenOrNot = (value) => {
const validate = emptyValidate('openOrNot', value)['openOrNot'];
@ -492,6 +513,14 @@ export const validateOpenOrNot = (value) => {
payload: payloadInfo('openOrNot', value, errMsg, validate)
}
};
// 保存知识点
export const saveTagDisciplineId = (value) => {
// console.log('====????????????', value);
return {
type: types.SAVE_TAG_DISCIPLINE_ID,
payload: value
};
}
// 新增测试用例
export const addTestCase = (obj) => {
return {
@ -622,7 +651,6 @@ export const testCaseOutputChange = (value, index) => {
// // 调试代码时,更改对应的状态值
// export const changeTestCodeStatus = () => {
// 更新测试用命及验证
export const updateTestAndValidate = (obj) => {
return (dispatch) => {
@ -644,3 +672,25 @@ export const updateOpenTestCaseIndex = (value) => {
payload: value
}
}
// 获取课程题库
export const getQuestion = (params) => {
return (dispatch) => {
fetchQuestion(params).then(res => {
const { data = {} } = res;
const { disciplines = [] } = data;
dispatch({
type: types.GET_COURSE_QUESTION,
payload: disciplines
})
})
}
}
// 保存所选择的知识点
export const saveKnowledge = (values) => {
return {
type: types.CHANGE_KNOWLEDGES,
payload: values
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:41:48
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 21:28:28
* @LastEditTime : 2020-01-02 14:24:09
*/
import types from "../actions/actionTypes";
import { Base64 } from 'js-base64';
@ -94,10 +94,11 @@ const ojForUserReducer = (state = initialState, action) => {
pages: Object.assign({}, state.pages, { total: records_count })
}
case types.SAVE_USER_CODE:
let curCode = Base64.encode(action.payload);
// console.log('save_user_code: ', action.payload);
// let curCode = Base64.encode(action.payload);
return {
...state,
userCode: curCode,
userCode: action.payload,
isUpdateCode: true,
}
case types.IS_UPDATE_CODE:
@ -136,7 +137,6 @@ const ojForUserReducer = (state = initialState, action) => {
} else {
curHack['code'] = '';
}
console.log(curHack);
return {
...state,
hack: Object.assign({}, state.hack, curHack),

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 16:40:32
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 20:00:26
* @LastEditTime : 2020-01-02 17:36:40
*/
import { Base64 } from 'js-base64';
import types from '../actions/actionTypes';
@ -15,10 +15,12 @@ const init = {
language: '',
description: '',
difficult: '',
category: '',
sub_discipline_id: '', // 方向
// category: '',
// openOrNot: 1,
timeLimit: 3
},
tag_discipline_id: [], // 知识点
ojFormValidate: {
name: {
validateStatus: '',
@ -36,14 +38,18 @@ const init = {
validateStatus: '',
errMsg: ''
},
category: {
validateStatus: '',
errMsg: ''
},
// category: {
// validateStatus: '',
// errMsg: ''
// },
// openOrNot: {
// validateStatus: '',
// errMsg: ''
// },
sub_discipline_id: {
validateStatus: '',
errMsg: ''
},
timeLimit: {
validateStatus: '',
errMsg: ''
@ -67,6 +73,8 @@ const init = {
testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成)
openTestCodeIndex: [0], // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个
isPublish: 0, // 是否是发布状态: 0 未发布 1 已发布
courseQuestions: [], // 课程题库
knowledges: [], // 知识点下拉值
}
const tcValidateObj = {
@ -127,10 +135,17 @@ const ojFormReducer = (state = initialState, action) => {
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_CATEGORY:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_SUB_DISCIPLINE_ID:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_OPENORNOT:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_TIMELIMIT:
return returnState(state, ojForm, ojFormValidate);
case types.SAVE_TAG_DISCIPLINE_ID:
return {
...state,
tag_discipline_id: action.payload
}
case types.ADD_TEST_CASE:
const { testCase, tcValidate } = action.payload;
const tcArrs = state.testCases.concat([testCase]);
@ -174,7 +189,8 @@ const ojFormReducer = (state = initialState, action) => {
* 6. 更改测试用例状态
* 7. 添加测试用例验证
*/
const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status } = action.payload;
const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status, sub_discipline_id, tag_discipline_id } = action.payload;
const { courseQuestions } = state;
let desc = null;
try {
desc = JSON.parse(description)
@ -188,7 +204,8 @@ const ojFormReducer = (state = initialState, action) => {
difficult,
category,
openOrNot: 1,
timeLimit: time_limit
timeLimit: time_limit,
sub_discipline_id
};
// state.code = code; // 保存代码块值
let curPosition = 0;
@ -208,6 +225,18 @@ const ojFormReducer = (state = initialState, action) => {
} else if (Array.isArray(code)) {
cbcode = Base64.decode(code[code.length - 1]);
}
// console.log('++++>>>>>>>>>>>>>', courseQuestions);
let temp_knowledges = [];
courseQuestions.forEach(c => {
if (sub_discipline_id && c.sub_disciplines) {
c.sub_disciplines.forEach(sub => {
if (+sub.id === sub_discipline_id) {
temp_knowledges = sub.tag_disciplines || [];
}
});
}
});
// state.position = curPosition; // 计算下一个测试用例的位置值
return {
...state,
@ -218,7 +247,9 @@ const ojFormReducer = (state = initialState, action) => {
testCasesValidate: curTcValidates,
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default',
isPublish: status,
showCode: cbcode
showCode: cbcode,
tag_discipline_id,
knowledges: temp_knowledges
}
case types.CHANGE_PUBLISH_VALUE:
return {
@ -301,11 +332,22 @@ const ojFormReducer = (state = initialState, action) => {
if (tIndex === -1) {
tempArr.push(action.payload);
}
console.log(tempArr);
// console.log(tempArr);
return {
...state,
openTestCodeIndex: tempArr
}
case types.GET_COURSE_QUESTION:
return {
...state,
courseQuestions: action.payload
}
case types.CHANGE_KNOWLEDGES: {
return {
...state,
knowledges: action.payload
}
}
default:
return state;
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:55:38
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 11:06:27
* @LastEditTime : 2019-12-30 09:44:56
*/
import axios from 'axios';
@ -142,4 +142,10 @@ export async function fetchUploadImageUrl (id) {
export async function fetchAddNotes (identifier, params) {
const url = `/myproblems/${identifier}/add_notes.json`;
return axios.post(url, params);
}
// 获取课程体系
export async function fetchQuestion (params) {
const url = `/disciplines.json`;
return axios.get(url, { params });
}
Loading…
Cancel
Save