commit
						b301628705
					
				| @ -1,16 +1,16 @@ | ||||
| class AddUniqIndexToPollVotes < ActiveRecord::Migration[5.2] | ||||
|   def change | ||||
|     remove_index :poll_votes, column: [:poll_question_id, :user_id] | ||||
| 
 | ||||
|     change_column_default :poll_votes, :poll_question_id, from: nil, to: -1 | ||||
|     PollVote.where(poll_answer_id: nil).update_all(poll_answer_id: -1) | ||||
| 
 | ||||
|     sql = %Q(delete from poll_votes where (poll_question_id, user_id, poll_answer_id) in | ||||
|             (select * from (select poll_question_id, user_id, poll_answer_id from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1) a) | ||||
|             and id not in (select * from (select min(id) from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1 order by id) b)) | ||||
|     ActiveRecord::Base.connection.execute sql | ||||
| 
 | ||||
|     add_index :poll_votes, [:poll_question_id, :user_id, :poll_answer_id], name: 'poll_answer_index', unique: true | ||||
|     # remove_index :poll_votes, column: [:poll_question_id, :user_id] | ||||
|     # | ||||
|     # change_column_default :poll_votes, :poll_question_id, from: nil, to: -1 | ||||
|     # PollVote.where(poll_answer_id: nil).update_all(poll_answer_id: -1) | ||||
|     # | ||||
|     # sql = %Q(delete from poll_votes where (poll_question_id, user_id, poll_answer_id) in | ||||
|     #         (select * from (select poll_question_id, user_id, poll_answer_id from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1) a) | ||||
|     #         and id not in (select * from (select min(id) from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1 order by id) b)) | ||||
|     # ActiveRecord::Base.connection.execute sql | ||||
|     # | ||||
|     # add_index :poll_votes, [:poll_question_id, :user_id, :poll_answer_id], name: 'poll_answer_index', unique: true | ||||
| 
 | ||||
|   end | ||||
| end | ||||
|  | ||||
| @ -0,0 +1,5 @@ | ||||
| class AddIndexToExerciseAnswers < ActiveRecord::Migration[5.2] | ||||
|   def change | ||||
|     add_index :exercise_answers, [:exercise_question_id, :user_id], name: "index_on_question_id_user_id" | ||||
|   end | ||||
| end | ||||
| @ -0,0 +1,16 @@ | ||||
| class AddIndexToExerciseChoice < ActiveRecord::Migration[5.2] | ||||
|   def change | ||||
|     change_column_default :exercise_answers, :exercise_choice_id, from: nil, to: -1 | ||||
|     ExerciseAnswer.where(exercise_choice_id: nil).update_all(exercise_choice_id: -1) | ||||
| 
 | ||||
|     sql = %Q(delete from exercise_answers where (exercise_question_id, user_id, exercise_choice_id) in | ||||
|             (select * from (select exercise_question_id, user_id, exercise_choice_id from exercise_answers group by exercise_question_id, user_id, exercise_choice_id having count(*) > 1) a) | ||||
|             and id not in (select * from (select min(id) from exercise_answers group by exercise_question_id, user_id, exercise_choice_id having count(*) > 1 order by id) b)) | ||||
|     ActiveRecord::Base.connection.execute sql | ||||
| 
 | ||||
|     add_index :exercise_answers, [:exercise_question_id, :user_id, :exercise_choice_id], name: 'exercise_choice_index', unique: true | ||||
| 
 | ||||
|     remove_index :exercise_answers, name: :index_on_question_id_user_id | ||||
| 
 | ||||
|   end | ||||
| end | ||||
| @ -0,0 +1,15 @@ | ||||
| class AddIndexToPollAnswer < ActiveRecord::Migration[5.2] | ||||
|   def change | ||||
|     remove_index :poll_votes, column: [:poll_question_id, :user_id] | ||||
| 
 | ||||
|     change_column_default :poll_votes, :poll_question_id, from: nil, to: -1 | ||||
|     PollVote.where(poll_answer_id: nil).update_all(poll_answer_id: -1) | ||||
| 
 | ||||
|     sql = %Q(delete from poll_votes where (poll_question_id, user_id, poll_answer_id) in | ||||
|             (select * from (select poll_question_id, user_id, poll_answer_id from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1) a) | ||||
|             and id not in (select * from (select min(id) from poll_votes group by poll_question_id, user_id, poll_answer_id having count(*) > 1 order by id) b)) | ||||
|     ActiveRecord::Base.connection.execute sql | ||||
| 
 | ||||
|     add_index :poll_votes, [:poll_question_id, :user_id, :poll_answer_id], name: 'poll_answer_index', unique: true | ||||
|   end | ||||
| end | ||||
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								| @ -0,0 +1,334 @@ | ||||
| /* | ||||
|  * @Description:  | ||||
|  * @Author: tangjiang | ||||
|  * @Github:  | ||||
|  * @Date: 2019-12-01 09:17:07 | ||||
|  * @LastEditors: tangjiang | ||||
|  * @LastEditTime: 2019-12-02 16:33:35 | ||||
|  */ | ||||
| import 'quill/dist/quill.core.css'; | ||||
| import 'quill/dist/quill.bubble.css'; | ||||
| import 'quill/dist/quill.snow.css'; | ||||
| import './index.scss'; | ||||
| import React, { useState, useImperativeHandle, useRef, useEffect } from 'react'; | ||||
| import { Form, Input, InputNumber, Button, Select } from 'antd'; | ||||
| import { connect } from 'react-redux'; | ||||
| import AddTestDemo from './AddTestDemo'; | ||||
| import QuillEditor from '../../../quillEditor'; | ||||
| import actions from '../../../../../redux/actions'; | ||||
| import CONST from '../../../../../constants'; | ||||
| 
 | ||||
| const {jcLabel} = CONST; | ||||
| const { Option } = Select; | ||||
| const FormItem = Form.Item; | ||||
| 
 | ||||
| const maps = { | ||||
|   language: [ | ||||
|     { title: 'C', key: 'C' }, | ||||
|     { title: 'C++', key: 'C++' }, | ||||
|     { title: 'Python', key: 'Python' }, | ||||
|     { title: 'Java', key: 'Java' } | ||||
|   ], | ||||
|   difficult: [ | ||||
|     { title: '简单', key: '1' }, | ||||
|     { title: '中等', key: '2'}, | ||||
|     { title: '困难', key: '3' } | ||||
|   ], | ||||
|   category: [ | ||||
|     { title: '程序设计', key: '1' }, | ||||
|     { title: '算法', key: '2'} | ||||
|   ], | ||||
|   openOrNot: [ | ||||
|     { title: '公开', key: '1' }, | ||||
|     { title: '私有', key: '0' } | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| function EditTab (props, ref) { | ||||
| 
 | ||||
|   const {  | ||||
|     form, | ||||
|     ojForm, | ||||
|     position,  | ||||
|     testCases, | ||||
|     addTestCase, | ||||
|     deleteTestCase, | ||||
|     testCasesValidate, | ||||
|     getFormData | ||||
|   } = props; | ||||
| 
 | ||||
|   const { getFieldDecorator } = form; | ||||
| 
 | ||||
|   const formRef = useRef(null); | ||||
|   const [description, setDescription] = useState(''); | ||||
| 
 | ||||
|   // 获取表单label
 | ||||
|   const myLabel = (name, subTitle) => { | ||||
|     if (subTitle) { | ||||
|       return ( | ||||
|         <span className={'label_text'}> | ||||
|           {name} | ||||
|           <span className={'label_sub_text'}> | ||||
|             ({subTitle}) | ||||
|           </span> | ||||
|         </span> | ||||
|       ) | ||||
|     } else { | ||||
|       return ( | ||||
|         <span className={'label_text'}>{name}</span> | ||||
|       ) | ||||
|     } | ||||
|   }; | ||||
|   // 获取下拉列表项
 | ||||
|   const getOptions = (key) => { | ||||
|     return maps[key].map((opt, i) => { | ||||
|       return ( | ||||
|         <Option value={opt.key} key={`opt_${i}`}>{opt.title}</Option> | ||||
|       ); | ||||
|     }); | ||||
|   }; | ||||
|   // 向外暴露的方法
 | ||||
|   useImperativeHandle(ref, () => ({ | ||||
|     validateForm () { | ||||
|       props.form.validateFields((err, values) => { | ||||
|         if (!err) { | ||||
|           getFormData(() => { | ||||
|             return values;  | ||||
|           }); | ||||
|         } else { | ||||
|           return; | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   })); | ||||
|   // 添加测试用例
 | ||||
|   const handleAddTest = () => { | ||||
|     const obj = { // 测试用例参数
 | ||||
|       input: '', | ||||
|       output: '', | ||||
|       position: position, | ||||
|       isAdd: true // 新增的测试用例
 | ||||
|     } | ||||
|     const validateObj = { // 测试用例验证参数
 | ||||
|       input: { | ||||
|         validateStatus: '', | ||||
|         errMsg: '' | ||||
|       }, | ||||
|       output: { | ||||
|         validateStatus: '', | ||||
|         errMsg: '' | ||||
|       } | ||||
|     } | ||||
|     addTestCase({testCase: obj, tcValidate: validateObj}); | ||||
|     // TODO 点击新增时,需要滚到到最底部
 | ||||
|     // this.editorRef.current.scrollTop
 | ||||
|     // const oDiv = this.editorRef.current;
 | ||||
|     // oDiv.scrollTo(oDiv.scrollLeft, 99999);
 | ||||
|     // console.log(oDiv.scrollTop);
 | ||||
|     // oDiv.scrollTop = 99999;
 | ||||
|   } | ||||
|   // 渲染测试用例
 | ||||
|   const renderTestCase = () => { | ||||
|     return testCases.map((item, i) => { | ||||
|       return ( | ||||
|         <AddTestDemo | ||||
|           key={`key_${i}`} | ||||
|           onSubmitTest={handleSubmitTest}  | ||||
|           onDeleteTest={handleDeleteTest}  | ||||
|           testCase={item} | ||||
|           testCaseValidate={testCasesValidate[i]} | ||||
|           index={i} | ||||
|         /> | ||||
|       ) | ||||
|     }); | ||||
|   }; | ||||
|   // 提交测试用例
 | ||||
|   const handleSubmitTest = (obj) => { | ||||
|     console.log('提交的测试用例: ', obj); | ||||
|   }; | ||||
|   // 删除测试用例
 | ||||
|   const handleDeleteTest = (obj) => { | ||||
|     console.log('删除的测试用例: ', obj); | ||||
|     deleteTestCase(obj); | ||||
|   }; | ||||
|   // 描述信息改变时
 | ||||
|   const handleChangeDescription = (value) => { | ||||
|     console.log('描述信息改变: ', value); | ||||
|     if (value) { | ||||
|       setDescription(value); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (description) { | ||||
|       props.form.setFieldsValue({ | ||||
|         description: description | ||||
|       }, function () { | ||||
|         console.log('设置成功。。。'); | ||||
|       }); | ||||
|     } | ||||
|   }, [description]); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={'editor_area'}> | ||||
|       <Form  | ||||
|         hideRequiredMark={true} | ||||
|         className={'editor_form'}  | ||||
|         ref={formRef}> | ||||
|         <FormItem | ||||
|           className={`input_area flex_60`} | ||||
|           label={<span>{myLabel(jcLabel['name'])}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('name', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '任务名称不能为空' } | ||||
|               ], | ||||
|               initialValue: ojForm.name | ||||
|             })(<Input placeholder="请输入任务名称"/>) | ||||
|           } | ||||
|         </FormItem> | ||||
|          | ||||
|         <FormItem | ||||
|           className={`input_area flex_40`} | ||||
|           label={<span>{myLabel(jcLabel['language'])}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('language', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '语言不能为空' } | ||||
|               ], | ||||
|               initialValue: ojForm.language | ||||
|             })( | ||||
|               <Select> | ||||
|                 {getOptions('language')} | ||||
|               </Select>) | ||||
|           } | ||||
|         </FormItem> | ||||
| 
 | ||||
|         <FormItem | ||||
|           className={`input_area flex_100`} | ||||
|           label={<span>{myLabel(jcLabel['description'])}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('description', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '描述信息不能为空' } | ||||
|               ], | ||||
|               initialValue: ojForm.description | ||||
|             })(<QuillEditor | ||||
|                 style={{ height: '300px' }} | ||||
|                 placeholder="请输入描述信息" | ||||
|                 htmlCtx={ojForm.description} | ||||
|                 onEditorChange={handleChangeDescription} | ||||
|               />) | ||||
|           } | ||||
|         </FormItem> | ||||
| 
 | ||||
|         <FormItem | ||||
|           className={`input_area flex_50 flex_50_left`} | ||||
|           label={<span>{myLabel(jcLabel['difficult'], '任务的难易程度')}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('difficult', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '难度不能为空' } | ||||
|               ], | ||||
|               initialValue: `${ojForm.difficult || ''}` | ||||
|             })( | ||||
|               <Select> | ||||
|                 {getOptions('difficult')} | ||||
|               </Select> | ||||
|             ) | ||||
|           } | ||||
|         </FormItem> | ||||
| 
 | ||||
|         <FormItem | ||||
|           className={`input_area flex_50 flex_50_right`} | ||||
|           label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('timeLimit', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '时间限制不能为空' } | ||||
|               ], | ||||
|               initialValue: ojForm.timeLimit | ||||
|             })(<InputNumber min={0} style={{ width: '100%' }} />) | ||||
|           } | ||||
|         </FormItem> | ||||
| 
 | ||||
|         <FormItem | ||||
|           className={`input_area flex_50 flex_50_left`} | ||||
|           label={<span>{myLabel(jcLabel['category'], '任务所属分类')}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('category', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '任务名称不能为空' } | ||||
|               ], | ||||
|               initialValue: `${ojForm.category || ''}` | ||||
|             })( | ||||
|               <Select> | ||||
|                 {getOptions('category')} | ||||
|               </Select> | ||||
|             ) | ||||
|           } | ||||
|         </FormItem> | ||||
| 
 | ||||
|         <FormItem | ||||
|           className={`input_area flex_50 flex_50_right`} | ||||
|           label={<span>{myLabel(jcLabel['openOrNot'])}</span>} | ||||
|         > | ||||
|           { | ||||
|             getFieldDecorator('openOrNot', { | ||||
|               rules: [ | ||||
|                 { required: true, message: '任务名称不能为空' } | ||||
|               ], | ||||
|               initialValue: `${ojForm.openOrNot}` | ||||
|             })( | ||||
|               <Select> | ||||
|                 {getOptions('openOrNot')} | ||||
|               </Select> | ||||
|             ) | ||||
|           } | ||||
|         </FormItem> | ||||
|       </Form> | ||||
|       {/* 添加测试用例 */} | ||||
|       <div className="test_demo_title"> | ||||
|           <h2>测试用例</h2> | ||||
|           <Button type="primary" onClick={handleAddTest}>添加测试用例</Button> | ||||
|         </div> | ||||
|         <div className="test_demo_ctx"> | ||||
|           { renderTestCase() } | ||||
|         </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| const mapStateToProps = (state) => { | ||||
|   const ojFormReducer = state.ojFormReducer; | ||||
|   const {ojForm, position, testCases, testCasesValidate} = ojFormReducer; | ||||
|   return { | ||||
|     ojForm, | ||||
|     testCases, | ||||
|     testCasesValidate, | ||||
|     position | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| const mapDispatchToProps = (dispatch) => ({ | ||||
|   // 新增测试用例
 | ||||
|   addTestCase: (value) => dispatch(actions.addTestCase(value)), | ||||
|   // 删除测试用例
 | ||||
|   deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)), | ||||
| }) | ||||
| 
 | ||||
| // EditTab = React.formRef(EditTab);
 | ||||
| // EditTab = React.forwardRef(EditTab);
 | ||||
| 
 | ||||
| export default connect( | ||||
|   mapStateToProps, | ||||
|   mapDispatchToProps | ||||
| )(Form.create()( | ||||
|   React.forwardRef(EditTab) | ||||
| )); | ||||
| @ -1,25 +1 @@ | ||||
| // .split-pane-left{ | ||||
| //   .ant-tabs-nav-wrap{ | ||||
| //     padding: 0 30px; | ||||
| //   } | ||||
| //   .ant-tabs-bar{ | ||||
| //     margin: 0; | ||||
| //   } | ||||
| //   // .ant-tabs-tabpane{ | ||||
| //   //   padding-top: 10px; | ||||
| //   //   height: calc(100vh - 110px); | ||||
| //   //   overflow: auto; | ||||
| //   // } | ||||
| 
 | ||||
| //   .ant-form-item-control{ | ||||
| //     line-height: 1; | ||||
| //   } | ||||
| 
 | ||||
| //   .editor_area, | ||||
| //   .prev_area{ | ||||
| //     height: calc(100vh - 110px); | ||||
| //     overflow-y: auto; | ||||
| //     padding: 20px 0; | ||||
| //   } | ||||
| // } | ||||
| @import '../../split_pane_resizer.scss'; | ||||
|  | ||||
| @ -1,215 +1,63 @@ | ||||
| /* | ||||
|  * @Description: 右侧代码块  | ||||
|  * @Description:  | ||||
|  * @Author: tangjiang | ||||
|  * @Date: 2019-11-18 08:42:04  | ||||
|  * @Last Modified by: tangjiang | ||||
|  * @Last Modified time: 2019-11-20 00:00:34 | ||||
|  * @Github:  | ||||
|  * @Date: 2019-12-01 10:18:35 | ||||
|  * @LastEditors: tangjiang | ||||
|  * @LastEditTime: 2019-12-03 09:11:50 | ||||
|  */ | ||||
| 
 | ||||
| import './index.scss'; | ||||
| 
 | ||||
| import React, { Fragment, useState, useRef, useEffect } from 'react'; | ||||
| import { Icon, Drawer, Tabs, Button, notification } from 'antd'; | ||||
| import _ from 'lodash'; | ||||
| import MonacoEditor from '@monaco-editor/react'; | ||||
| import React from 'react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import InitTabCtx from './initTabCtx'; | ||||
| import SettingDrawer from '../../components/monacoSetting'; | ||||
| import CONST from '../../../../constants'; | ||||
| import MyMonacoEditor from '../../components/myMonacoEditor'; | ||||
| import ControlSetting from '../../components/controlSetting'; | ||||
| import actions from '../../../../redux/actions'; | ||||
| 
 | ||||
| const { fontSetting, opacitySetting } = CONST; | ||||
| 
 | ||||
| const { TabPane } = Tabs; | ||||
| 
 | ||||
| const RightPaneCode = (props) => { | ||||
| 
 | ||||
|   const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
 | ||||
|   const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
 | ||||
|   const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
 | ||||
|   const [editCode, setEditCode] = useState(()=> { | ||||
|     return '#include <stdio.h>'; | ||||
|   });  // monaco编辑器内容
 | ||||
|   const [language, setLanguage] = useState('C') | ||||
|   const [fontSize,setFontSize] = useState(12); | ||||
|   const editorRef = useRef(null); // 编辑器ref
 | ||||
| function RightPane (props, ref) { | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (props.language) { | ||||
|       // console.log('当前输入的代码:', editCode);
 | ||||
|       // console.log('当前输入的语言:', props.language);
 | ||||
|       setLanguage(props.language) | ||||
|     } | ||||
|   }, [props.language]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|   }, [props.testCases]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|   }, [editCode]); | ||||
|   const { | ||||
|     // identifier,
 | ||||
|     onSubmitForm, | ||||
|     saveOjFormCode | ||||
|   } = props; | ||||
|    | ||||
|   // 监听store中编辑器内容变化
 | ||||
|   useEffect(() => { | ||||
|     setEditCode(props.code); | ||||
|   }, [props.code]); | ||||
| 
 | ||||
|   // 打开设置
 | ||||
|   const handleShowDrawer = (e) => { | ||||
|     e.preventDefault(); | ||||
|     setShowDrawer(true); | ||||
|   } | ||||
| 
 | ||||
|   // 关闭设置
 | ||||
|   const handleDrawerClose = (e) => { | ||||
|     e.preventDefault(); | ||||
|     setShowDrawer(false); | ||||
|   } | ||||
| 
 | ||||
|   // 切换tab
 | ||||
|   const handleTabChange = (key) => { | ||||
|     setDefaultActiveKey(key); | ||||
|   } | ||||
| 
 | ||||
|   // 显示/隐藏tab
 | ||||
|   const handleShowControl = () => { | ||||
|     setShowTextResult(!showTextResult); | ||||
|   } | ||||
| 
 | ||||
|   // 侧边栏改变字体大小
 | ||||
|   const handleFontSizeChange = (value) => { | ||||
|     setFontSize(value); | ||||
|   } | ||||
|   // 文本框内容变化时,记录文本框内容
 | ||||
|   const handleEditorChange = (origin, monaco) => { | ||||
|     editorRef.current = monaco; // 获取当前monaco实例
 | ||||
|     setEditCode(origin); // 保存编辑器初始值
 | ||||
|     editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
 | ||||
|       // TODO 需要优化 节流
 | ||||
|       const val = editorRef.current.getValue(); | ||||
|       setEditCode(val);  | ||||
|       // 保存当前代码
 | ||||
|       props.saveOjFormCode(val); | ||||
|     }); | ||||
|   // 代码改变时,保存
 | ||||
|   const handleCodeChange = (code) => { | ||||
|     // 保存用户输入的代码
 | ||||
|     saveOjFormCode(code); | ||||
|   } | ||||
| 
 | ||||
|   // 提交按钮点击
 | ||||
|   const handleSubmit = (e) => { | ||||
|     e.preventDefault(); | ||||
|     if (!editCode) { | ||||
|       notification['error']({ | ||||
|         message: '必填', | ||||
|         description: '代码块内容必须输入!' | ||||
|       }); | ||||
|       editorRef.current.focus(); | ||||
|       return; | ||||
|     } | ||||
|     props.changePublishLoadingStatus(true); | ||||
|     const { onSubmitForm } = props; | ||||
|     onSubmitForm(editCode);  | ||||
|   } | ||||
| 
 | ||||
|   // 调试测试代码
 | ||||
|   // const handleTestCode = () => {
 | ||||
|   //   // 打开控制台信息
 | ||||
|   //   setShowTextResult(true);
 | ||||
|   //   this.formRef.handleTestCodeFormSubmit(() => {
 | ||||
|   //     // 当验证通过后 切换tab 到代码执行结果
 | ||||
|   //     setDefaultActiveKey('2');
 | ||||
|   //   });
 | ||||
|   // 启动调试代码
 | ||||
|   // const handleDebuggerCode = (value) => {
 | ||||
|   //   console.log('调用的代码调试====', value);
 | ||||
|   // }
 | ||||
|    | ||||
|   // 控制台点击时 添加active属性
 | ||||
|   const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`; | ||||
| 
 | ||||
|   // 配置编辑器属性
 | ||||
|   const editorOptions = { | ||||
|     selectOnLineNumbers: true, | ||||
|     automaticLayout: true, | ||||
|     fontSize: `${fontSize}px` | ||||
|   } | ||||
| 
 | ||||
|   // 返回渲染值
 | ||||
|   return ( | ||||
|     <Fragment> | ||||
|     <div className={'right_pane_code_wrap'}> | ||||
|         <div className={'code-title'}> | ||||
|           <span></span> | ||||
|           <Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/> | ||||
|         </div> | ||||
|         {/** 代码编辑器 */} | ||||
|         <MonacoEditor | ||||
|           height={showTextResult ? 'calc(100% - 382px)' : 'calc(100% - 112px)'} | ||||
|           width="100%" | ||||
|           language={language.toLowerCase()} | ||||
|           value={editCode} | ||||
|           options={editorOptions} | ||||
|           theme="dark" | ||||
|           editorDidMount={handleEditorChange} | ||||
|       <MyMonacoEditor language={props.language} code={props.code} onCodeChange={handleCodeChange}/> | ||||
|       <ControlSetting | ||||
|         // identifier={identifier}
 | ||||
|         inputValue={props.input}  | ||||
|         onSubmitForm={onSubmitForm} | ||||
|         // onDebuggerCode={handleDebuggerCode}
 | ||||
|       /> | ||||
|         {/* 控制台信息 */} | ||||
|         <div className="pane_control_area"> | ||||
|           <Tabs | ||||
|             className={classNames} | ||||
|             activeKey={defaultActiveKey}  | ||||
|             tabBarStyle={{ backgroundColor: '#000', color: '#fff' }} | ||||
|             onChange={handleTabChange} | ||||
|           > | ||||
|             <TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}> | ||||
|               <InitTabCtx wrappedComponentRef={(form) => this.formRef = form }/> | ||||
|             </TabPane> | ||||
|             <TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}> | ||||
|               <h2>代码执行结果</h2> | ||||
|             </TabPane> | ||||
|           </Tabs> | ||||
|           <div className="pane_control_opts"> | ||||
|             <Button type="link" style={{ color: '#fff' }} onClick={handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button> | ||||
|             <p> | ||||
|               {/* <Button ghost  | ||||
|                 style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}  | ||||
|                 onClick={handleTestCode} | ||||
|                 disabled={!props.identifier || props.testCases.length === 0} | ||||
|               >调试代码</Button> */} | ||||
|               <Button  | ||||
|                 loading={props.submitLoading} | ||||
|                 type="primary" | ||||
|                 onClick={handleSubmit} | ||||
|             >{props.identifier ? '更新' : '提交'}</Button> | ||||
|             </p> | ||||
|           </div> | ||||
|         </div> | ||||
|     </div> | ||||
|       <Drawer | ||||
|         className={'setting_drawer'} | ||||
|         placement="right" | ||||
|         closable={false} | ||||
|         onClose={handleDrawerClose} | ||||
|         visible={showDrawer} | ||||
|       > | ||||
|         <SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/> | ||||
|         <SettingDrawer {...opacitySetting}/> | ||||
|       </Drawer> | ||||
|     </Fragment> | ||||
|   ); | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| const mapStateToProps = (state) => { | ||||
|   const { ojForm, testCases, identifier, code } = state.ojFormReducer; | ||||
|   const { submitLoading } = state.commonReducer; | ||||
|   const { ojForm, testCases, code, identifier } = state.ojFormReducer; | ||||
|   return { | ||||
|     language: ojForm.language, | ||||
|     testCases, | ||||
|     identifier, | ||||
|     code, | ||||
|     submitLoading | ||||
|     identifier, | ||||
|     language: ojForm.language, | ||||
|     input: (testCases[0] && testCases[0].input) || '', | ||||
|      | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const mapDispatchToProps = (dispatch) => ({ | ||||
|   saveOjFormCode: (code) => dispatch(actions.saveOjFormCode(code)), | ||||
|   changePublishLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)) | ||||
|   // 保存提交的代码值
 | ||||
|   saveOjFormCode: (value) => dispatch(actions.saveOjFormCode(value)), | ||||
| }); | ||||
| // 
 | ||||
| export default connect( | ||||
|   mapStateToProps, | ||||
|   mapDispatchToProps | ||||
| )(RightPaneCode); | ||||
| )(RightPane); | ||||
|  | ||||
| @ -0,0 +1,215 @@ | ||||
| /* | ||||
|  * @Description: 右侧代码块  | ||||
|  * @Author: tangjiang  | ||||
|  * @Date: 2019-11-18 08:42:04  | ||||
|  * @Last Modified by: tangjiang | ||||
|  * @Last Modified time: 2019-11-20 00:00:34 | ||||
|  */ | ||||
| 
 | ||||
| import './index.scss'; | ||||
| 
 | ||||
| import React, { Fragment, useState, useRef, useEffect } from 'react'; | ||||
| import { Icon, Drawer, Tabs, Button, notification } from 'antd'; | ||||
| import _ from 'lodash'; | ||||
| import MonacoEditor from '@monaco-editor/react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import InitTabCtx from './initTabCtx'; | ||||
| import SettingDrawer from '../../components/monacoSetting'; | ||||
| import CONST from '../../../../constants'; | ||||
| import actions from '../../../../redux/actions'; | ||||
| 
 | ||||
| const { fontSetting, opacitySetting } = CONST; | ||||
| 
 | ||||
| const { TabPane } = Tabs; | ||||
| 
 | ||||
| const RightPaneCode = (props) => { | ||||
| 
 | ||||
|   const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
 | ||||
|   const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
 | ||||
|   const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
 | ||||
|   const [editCode, setEditCode] = useState(()=> { | ||||
|     return '#include <stdio.h>'; | ||||
|   });  // monaco编辑器内容
 | ||||
|   const [language, setLanguage] = useState('C') | ||||
|   const [fontSize,setFontSize] = useState(12); | ||||
|   const editorRef = useRef(null); // 编辑器ref
 | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (props.language) { | ||||
|       // console.log('当前输入的代码:', editCode);
 | ||||
|       // console.log('当前输入的语言:', props.language);
 | ||||
|       setLanguage(props.language) | ||||
|     } | ||||
|   }, [props.language]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|   }, [props.testCases]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|   }, [editCode]); | ||||
| 
 | ||||
|   // 监听store中编辑器内容变化
 | ||||
|   useEffect(() => { | ||||
|     setEditCode(props.code); | ||||
|   }, [props.code]); | ||||
| 
 | ||||
|   // 打开设置
 | ||||
|   const handleShowDrawer = (e) => { | ||||
|     e.preventDefault(); | ||||
|     setShowDrawer(true); | ||||
|   } | ||||
| 
 | ||||
|   // 关闭设置
 | ||||
|   const handleDrawerClose = (e) => { | ||||
|     e.preventDefault(); | ||||
|     setShowDrawer(false); | ||||
|   } | ||||
| 
 | ||||
|   // 切换tab
 | ||||
|   const handleTabChange = (key) => { | ||||
|     setDefaultActiveKey(key); | ||||
|   } | ||||
| 
 | ||||
|   // 显示/隐藏tab
 | ||||
|   const handleShowControl = () => { | ||||
|     setShowTextResult(!showTextResult); | ||||
|   } | ||||
| 
 | ||||
|   // 侧边栏改变字体大小
 | ||||
|   const handleFontSizeChange = (value) => { | ||||
|     setFontSize(value); | ||||
|   } | ||||
|   // 文本框内容变化时,记录文本框内容
 | ||||
|   const handleEditorChange = (origin, monaco) => { | ||||
|     editorRef.current = monaco; // 获取当前monaco实例
 | ||||
|     setEditCode(origin); // 保存编辑器初始值
 | ||||
|     editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
 | ||||
|       // TODO 需要优化 节流
 | ||||
|       const val = editorRef.current.getValue(); | ||||
|       setEditCode(val);  | ||||
|       // 保存当前代码
 | ||||
|       props.saveOjFormCode(val); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   // 提交按钮点击
 | ||||
|   const handleSubmit = (e) => { | ||||
|     e.preventDefault(); | ||||
|     if (!editCode) { | ||||
|       notification['error']({ | ||||
|         message: '必填', | ||||
|         description: '代码块内容必须输入!' | ||||
|       }); | ||||
|       editorRef.current.focus(); | ||||
|       return; | ||||
|     } | ||||
|     props.changePublishLoadingStatus(true); | ||||
|     const { onSubmitForm } = props; | ||||
|     onSubmitForm(editCode);  | ||||
|   } | ||||
| 
 | ||||
|   // 调试测试代码
 | ||||
|   // const handleTestCode = () => {
 | ||||
|   //   // 打开控制台信息
 | ||||
|   //   setShowTextResult(true);
 | ||||
|   //   this.formRef.handleTestCodeFormSubmit(() => {
 | ||||
|   //     // 当验证通过后 切换tab 到代码执行结果
 | ||||
|   //     setDefaultActiveKey('2');
 | ||||
|   //   });
 | ||||
|   // }
 | ||||
|    | ||||
|   // 控制台点击时 添加active属性
 | ||||
|   const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`; | ||||
| 
 | ||||
|   // 配置编辑器属性
 | ||||
|   const editorOptions = { | ||||
|     selectOnLineNumbers: true, | ||||
|     automaticLayout: true, | ||||
|     fontSize: `${fontSize}px` | ||||
|   } | ||||
| 
 | ||||
|   // 返回渲染值
 | ||||
|   return ( | ||||
|     <Fragment> | ||||
|       <div className={'right_pane_code_wrap'}> | ||||
|         <div className={'code-title'}> | ||||
|           <span></span> | ||||
|           <Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/> | ||||
|         </div> | ||||
|         {/** 代码编辑器 */} | ||||
|         <MonacoEditor | ||||
|           height={showTextResult ? 'calc(100% - 382px)' : 'calc(100% - 112px)'} | ||||
|           width="100%" | ||||
|           language={language.toLowerCase()} | ||||
|           value={editCode} | ||||
|           options={editorOptions} | ||||
|           theme="dark" | ||||
|           editorDidMount={handleEditorChange} | ||||
|         /> | ||||
|         {/* 控制台信息 */} | ||||
|         <div className="pane_control_area"> | ||||
|           <Tabs | ||||
|             className={classNames} | ||||
|             activeKey={defaultActiveKey}  | ||||
|             tabBarStyle={{ backgroundColor: '#000', color: '#fff' }} | ||||
|             onChange={handleTabChange} | ||||
|           > | ||||
|             <TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}> | ||||
|               <InitTabCtx wrappedComponentRef={(form) => this.formRef = form }/> | ||||
|             </TabPane> | ||||
|             <TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}> | ||||
|               <h2>代码执行结果</h2> | ||||
|             </TabPane> | ||||
|           </Tabs> | ||||
|           <div className="pane_control_opts"> | ||||
|             <Button type="link" style={{ color: '#fff' }} onClick={handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button> | ||||
|             <p> | ||||
|               {/* <Button ghost  | ||||
|                 style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}  | ||||
|                 onClick={handleTestCode} | ||||
|                 disabled={!props.identifier || props.testCases.length === 0} | ||||
|               >调试代码</Button> */} | ||||
|               <Button  | ||||
|                 loading={props.submitLoading} | ||||
|                 type="primary" | ||||
|                 onClick={handleSubmit} | ||||
|             >{props.identifier ? '更新' : '提交'}</Button> | ||||
|             </p> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <Drawer | ||||
|         className={'setting_drawer'} | ||||
|         placement="right" | ||||
|         closable={false} | ||||
|         onClose={handleDrawerClose} | ||||
|         visible={showDrawer} | ||||
|       > | ||||
|         <SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/> | ||||
|         <SettingDrawer {...opacitySetting}/> | ||||
|       </Drawer> | ||||
|     </Fragment> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| const mapStateToProps = (state) => { | ||||
|   const { ojForm, testCases, identifier, code } = state.ojFormReducer; | ||||
|   const { submitLoading } = state.commonReducer; | ||||
|   return { | ||||
|     language: ojForm.language, | ||||
|     testCases, | ||||
|     identifier, | ||||
|     code, | ||||
|     submitLoading | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const mapDispatchToProps = (dispatch) => ({ | ||||
|   saveOjFormCode: (code) => dispatch(actions.saveOjFormCode(code)), | ||||
|   changePublishLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)) | ||||
| }); | ||||
| // 
 | ||||
| export default connect( | ||||
|   mapStateToProps, | ||||
|   mapDispatchToProps | ||||
| )(RightPaneCode); | ||||
					Loading…
					
					
				
		Reference in new issue