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

dev_chen
杨树明 5 years ago
commit d661b2b7b0

@ -3,7 +3,7 @@ class ExaminationBanksController < ApplicationController
before_action :require_login
before_action :certi_identity_auth, only: [:create, :edit, :update, :destroy, :set_public, :revoke_item, :cancel_items]
before_action :find_exam, except: [:index, :create, :cancel_items]
before_action :edit_auth, only: [:update, :set_public, :revoke_item, :cancel_items]
before_action :edit_auth, only: [:update, :set_public, :revoke_item]
before_action :identity_auth, only: [:index]
def index
@ -52,7 +52,7 @@ class ExaminationBanksController < ApplicationController
end
def destroy
tip_exception(403, "无权限") unless current_user.admin? || @item.user == current_user
tip_exception(403, "无权限") unless current_user.admin? || @exam.user == current_user
ActiveRecord::Base.transaction do
ApplyAction.where(container_type: "ExaminationBank", container_id: @exam.id).destroy_all
@exam.destroy!

@ -137,7 +137,7 @@ class ExercisesController < ApplicationController
:course_id => @course.id,
:time => -1,
:exercise_status => 1,
:is_md => params[:md]
:is_md => params[:is_md]
}
@exercise = Exercise.create!(exercise_options)
end

@ -1,74 +1,58 @@
import React,{ Component } from "react";
import React, { Component } from "react";
import TPMMDEditor from '../../../modules/tpm/challengesnew/TPMMDEditor'
import {markdownToHTML} from 'educoder'
import { markdownToHTML } from 'educoder'
import './DMDEditor.css'
// 需要父组件通过toShowMode、toMDMode 来控制一次只能打开一个DMDEditor
class DMDEditor extends Component{
constructor(props){
class DMDEditor extends Component {
constructor(props) {
super(props);
this.mdRef = React.createRef()
this.state={
mdMode: false,
// value: this.props.initValue
this.state = {
mdMode: false,
}
}
componentDidUpdate(prevProps, prevState) {
}
componentDidMount() {
// if(this.props.initValue != this.mdRef.current.getValue()) {
// this.mdRef.current.setValue(this.props.initValue)
// }
}
toMDMode = () => {
this.setState({mdMode: true}, () => {
this.mdRef.current.resize()
this.mdRef.current.setValue(this.props.initValue)
this.setState({ mdMode: true }, () => {
this.mdRef.current.resize()
this.mdRef.current.setValue(this.props.initValue)
})
this.props.toMDMode(this)
this.props.toMDMode(this)
}
toShowMode = () => {
this.setState({mdMode: false})
this.setState({ mdMode: false })
this.props.toShowMode && this.props.toShowMode(this)
}
onCMBlur = () => {
this.toShowMode()
this.toShowMode()
}
onChange = (val) => {
// this.setState({ value: val })
this.props.onChange(val)
if (this.state.showError == true) {
this.setState({showError: false})
this.setState({ showError: false })
}
}
showError = () => {
this.mdRef.current.showError()
this.setState({showError: true})
this.setState({ showError: true })
}
render(){
render() {
const { mdMode, showError } = this.state;
const { initValue } = this.props;
let _style = {}
if (showError) {
_style.border = '1px solid red'
_style.border = '1px solid red'
}
_style = Object.assign(_style, {display: mdMode == true ? 'none' : '', color: initValue? '': '#999', alignItems: 'center', wordBreak: 'break-all'})
return(
_style = Object.assign(_style, { display: mdMode == true ? 'none' : '', color: initValue ? '' : '#999', alignItems: 'center', wordBreak: 'break-all' })
return (
<React.Fragment>
<style>{`
`}</style>
<div id="content_editorMd_show" className="new_li content_editorMd_show markdown-body"
style={_style}
dangerouslySetInnerHTML={{__html: initValue ? markdownToHTML(initValue):this.props.placeholder}}
onClick={this.toMDMode}
style={_style}
dangerouslySetInnerHTML={{ __html: initValue ? markdownToHTML(initValue) : this.props.placeholder }}
onClick={this.toMDMode}
>
</div>
{/*
onCMBlur={this.onCMBlur} */}
<TPMMDEditor
ref={this.mdRef}
{...this.props}

@ -17,14 +17,13 @@ export default class ImageBlot extends BlockEmbed {
const node = super.create();
node.setAttribute('alt', value.alt);
node.setAttribute('src', value.url);
// console.log('~~~~~~~~~~~', node, value);
node.addEventListener('click', function () {
try {
value.onclick(value.url);
}catch (e) {
try {
value.onclick(value.url);
} catch (e) {
}
}, false);
}
}, false);
if (value.width) {
node.setAttribute('width', value.width);
}
@ -40,16 +39,13 @@ export default class ImageBlot extends BlockEmbed {
node.setAttribute('width', '100%');
}
// node.setAttribute('style', { cursor: 'pointer' });
node.setAttribute('style', { cursor: 'pointer' });
// if (node.onclick) {
// console.log('image 有图片点击事件======》》》》》》');
// // node.setAttribute('onclick', node.onCLick);
// }
// 给图片添加点击事件
// node.onclick = () => {
// value.onClick && value.onClick(value.url);
// }
if (value.onclick) {
node.addEventListener('click', () => {
value.onClick(value.url)
})
}
return node;
}

@ -1,30 +1,24 @@
/*
* @Description: quill 编辑器
* @Author: tangjiang
* @Github:
* @Date: 2019-12-18 08:49:30
* @LastEditors : tangjiang
* @LastEditTime : 2020-02-05 11:23:03
*/
import './index.scss';
import 'quill/dist/quill.core.css'; // 核心样式
import 'quill/dist/quill.snow.css'; // 有工具栏
import 'quill/dist/quill.bubble.css'; // 无工具栏
import 'katex/dist/katex.min.css'; // katex 表达式样式
import './index.scss'
import 'quill/dist/quill.core.css' // 核心样式
import 'quill/dist/quill.snow.css' // 有工具栏
import 'quill/dist/quill.bubble.css' // 无工具栏
import 'katex/dist/katex.min.css' // katex 表达式样式
import './font.css'
import React, { useState, useRef, useEffect } from 'react';
import Quill from 'quill';
import katex from 'katex';
import deepEqual from './deepEqual.js'
import { fetchUploadImage } from '../../services/ojService.js';
import React, { useState, useRef, useEffect } from 'react'
import Quill from 'quill'
import katex from 'katex'
import { fetchUploadImage } from '../../services/ojService.js'
import { getImageUrl } from 'educoder'
import ImageBlot from './ImageBlot';
import FillBlot from './FillBlot';
import ImageBlot from './ImageBlot'
import FillBlot from './FillBlot'
import LinkBlot from './link-blot'
var Size = Quill.import('attributors/style/size');
Size.whitelist = ['14px', '16px', '18px', '20px', false];
var fonts = ['Microsoft-YaHei', 'SimSun', 'SimHei', 'KaiTi', 'FangSong'];
var Font = Quill.import('formats/font');
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['14px', '16px', '18px', '20px', false]
let fonts = ['Microsoft-YaHei', 'SimSun', 'SimHei', 'KaiTi', 'FangSong']
let Font = Quill.import('formats/font')
Font.whitelist = fonts; //将字体加入到白名单
window.Quill = Quill;
window.katex = katex;
@ -32,14 +26,12 @@ Quill.register(ImageBlot);
Quill.register(Size);
Quill.register(LinkBlot);
Quill.register(Font, true);
// Quill.register({'modules/toolbar': Toolbar});
Quill.register({
'formats/fill': FillBlot
});
// Quill.register(Color);
})
function QuillForEditor({
export default ({
placeholder,
readOnly,
autoFocus = false,
@ -52,41 +44,23 @@ function QuillForEditor({
onContentChange,
addFill, // 点击填空成功的回调
deleteFill // 删除填空,返回删除的下标
}) {
// toolbar 默认值
const defaultConfig = [
}) => {
const defaultToolBarOptions = [
'bold', 'italic', 'underline',
{ size: ['14px', '16px', '18px', '20px'] },
{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, // 列表
{ align: [] }, { list: 'ordered' }, { list: 'bullet' },
{ script: 'sub' }, { script: 'super' },
{ 'color': [] }, { 'background': [] },
{ 'font': [] },
{ header: [1, 2, 3, 4, 5, false] },
'blockquote', 'code-block',
'link', 'image', 'video',
'blockquote', 'link', 'image', 'video',
'formula',
'clean'
];
const editorRef = useRef(null);
]
const editorRef = useRef(null)
// quill 实例
const [quill, setQuill] = useState(null);
const [selection, setSelection] = useState(null);
const [fillCount, setFillCount] = useState(0);
const [quillCtx, setQuillCtx] = useState({});
// 文本内容变化时
const handleOnChange = content => {
onContentChange && onContentChange(content, quill);
};
const renderOptions = options || defaultConfig;
const [quill, setQuill] = useState(null)
const [fillCount, setFillCount] = useState(0)
const bindings = {
tab: {
key: 9,
handler: function () { }
},
backspace: {
key: 'Backspace',
/**
@ -97,186 +71,139 @@ function QuillForEditor({
* @param {*} context 上下文
*/
handler: function (range, context) {
/**
* index: 删除元素的位置
* length: 删除元素的个数
*/
const { index, length } = range;
const _start = length === 0 ? index - 1 : index;
const _length = length || 1;
let delCtx = this.quill.getText(_start, _length); // 删除的元素
const { index, length } = range
const _start = length === 0 ? index - 1 : index
const _length = length || 1
let delCtx = this.quill.getText(_start, _length)
const reg = /▁/g;
const delArrs = delCtx.match(reg);
const delArrs = delCtx.match(reg)
if (delArrs) {
const r = window.confirm('确定要删除吗?');
const r = window.confirm('确定要删除吗?')
if (r) {
let leaveCtx; // 获取删除元素之前的内容
let leaveCtx = this.quill.getText(0, index) // 获取删除元素之前的内容
if (length === 0) {
leaveCtx = this.quill.getText(0, index - 1);
} else {
leaveCtx = this.quill.getText(0, index);
leaveCtx = this.quill.getText(0, index - 1)
}
const leaveArrs = leaveCtx.match(reg);
const leaveLen = (leaveArrs || []).length;
let delIndexs = [];
const leaveArrs = leaveCtx.match(reg)
const leaveLen = (leaveArrs || []).length
let delIndexs = []
// 获取删除元素的下标
delArrs.forEach((item, i) => {
leaveLen === 0 ? delIndexs.push(i) : delIndexs.push(leaveLen + i);
leaveLen === 0 ? delIndexs.push(i) : delIndexs.push(leaveLen + i)
});
deleteFill && deleteFill(delIndexs); // 调用删除回调, 返回删除的元素下标[]
deleteFill && deleteFill(delIndexs) // 调用删除回调, 返回删除的元素下标[]
return true
} else {
return false;
return false
}
}
return true;
return true
}
}
};
// quill 配置信息
}
const quillOption = {
modules: {
toolbar: renderOptions,
toolbar: options || defaultToolBarOptions,
keyboard: {
bindings: bindings
}
// toolbar: {
// container: renderOptions
// }
},
readOnly,
placeholder,
theme: readOnly ? 'bubble' : 'snow',
};
}
useEffect(() => {
const quillNode = document.createElement('div');
editorRef.current.appendChild(quillNode);
const _quill = new Quill(editorRef.current, quillOption);
setQuill(_quill);
const quillInstance = new Quill(editorRef.current, quillOption)
setQuill(quillInstance)
// 处理图片上传功能
_quill.getModule('toolbar').addHandler('image', (e) => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
quillInstance.getModule('toolbar').addHandler('image', (e) => {
const input = document.createElement('input')
input.setAttribute('type', 'file')
input.setAttribute('accept', 'image/*')
input.click()
input.onchange = async (e) => {
const file = input.files[0]; // 获取文件信息
const formData = new FormData();
formData.append('file', file);
const file = input.files[0] // 获取文件信息
const formData = new FormData()
formData.append('file', file)
const range = quillInstance.getSelection(true)
let fileUrl = '' // 保存上传成功后图片的url
const range = _quill.getSelection(true);
let fileUrl = ''; // 保存上传成功后图片的url
// 上传文件
const result = await fetchUploadImage(formData);
const result = await fetchUploadImage(formData)
// 获取上传图片的url
if (result.data && result.data.id) {
fileUrl = getImageUrl(`api/attachments/${result.data.id}`);
fileUrl = getImageUrl(`api/attachments/${result.data.id}`)
}
// 根据id获取文件路径
const { width, height } = imgAttrs;
// console.log('上传图片的url:', fileUrl);
const { width, height } = imgAttrs
if (fileUrl) {
_quill.insertEmbed(range.index, 'image', {
let imgOption = {
url: fileUrl,
alt: '图片信息',
onClick: showUploadImage,
width,
height
});
}
if (showUploadImage) {
imgOption.onClick = showUploadImage
}
quillInstance.insertEmbed(range.index, 'image', imgOption)
}
}
});
})
// 处理填空
_quill.getModule('toolbar').addHandler('fill', (e) => {
// alert(1111);
quillInstance.getModule('toolbar').addHandler('fill', (e) => {
setFillCount(fillCount + 1);
const range = _quill.getSelection(true);
_quill.insertText(range.index, '▁');
const range = quillInstance.getSelection(true);
quillInstance.insertText(range.index, '▁');
addFill && addFill(); // 调用添加回调
});
}, []);
})
}, [])
// 设置值
useEffect(() => {
if (!quill) return
const previous = quill.getContents()
if (value && value.hasOwnProperty('ops')) {
// console.log(value.ops);
try {
const ops = value.ops || [];
ops.forEach((item, i) => {
if (item.insert['image']) {
item.insert['image'] = Object.assign({}, item.insert['image'], { style: { cursor: 'pointer' }, onclick: (url) => showUploadImage(url) });
}
});
}catch (e) {
}
}
const current = value
if (!deepEqual(previous, current)) {
setSelection(quill.getSelection())
if (typeof value === 'string' && value) {
// debugger
quill.clipboard.dangerouslyPasteHTML(value, 'api');
if (autoFocus) {
quill.focus();
} else {
quill.blur();
}
} else {
if (quill) {
if (value && value.hasOwnProperty('ops')) {
quill.setContents(value)
if (autoFocus) quill.focus();
} else {
quill.pasteHTML(value)
}
}
}, [quill, value, setQuill, autoFocus]);
}, [quill, value])
// 清除选择区域
useEffect(() => {
if (quill && selection) {
quill.setSelection(selection)
setSelection(null)
if (quill) {
quill.root.dataset.placeholder = placeholder;
}
}, [quill, selection, setSelection]);
}, [quill, placeholder])
// 设置placeholder值
useEffect(() => {
if (!quill || !quill.root) return;
quill.root.dataset.placeholder = placeholder;
}, [quill, placeholder]);
if (quill) {
autoFocus ? quill.focus() : quill.blur()
}
}, [quill, autoFocus])
// 处理内容变化
useEffect(() => {
if (!quill) return;
if (typeof handleOnChange !== 'function') return;
let handler;
quill.on(
'text-change',
(handler = (delta, oldDelta, source) => {
const _ctx = quill.getContents();
setQuillCtx(_ctx);
handleOnChange(quill.getContents()); // getContents: 检索编辑器内容
})
);
return () => {
quill.off('text-change', handler);
if (quill && onContentChange) {
function onChangeHandler() {
let html = editorRef.current.children[0].innerHTML
let text = quill.getText()
if (html === '<p><br></p>') html = ''
if (onContentChange) {
onContentChange(quill.getContents(), quill, html, text)
}
}
quill.on('text-change', onChangeHandler)
return () => {
quill.off('text-change', onChangeHandler)
}
}
}, [quill, handleOnChange]);
}, [quill, onContentChange])
// 返回结果
return (
<div className='quill_editor_for_react_area' style={wrapStyle}>
<div ref={editorRef} style={style}></div>
</div>
);
)
}
export default QuillForEditor;

@ -75,12 +75,8 @@ class ExerciseNewCommon extends Component {
axios.get(url)
.then((response) => {
if (response.data.exercise) {
const { exercise, ...others } = response.data
exercise.exercise_name = exercise.exercise_name || exercise.name
exercise.exercise_description = exercise.exercise_description || exercise.description
this.setState({
...exercise,
...others,
...response.data,
editMode: false
})
this.props.initData && this.props.initData(response.data)
@ -397,6 +393,7 @@ class ExerciseNewCommon extends Component {
getAddQuestionUrl: this.getAddQuestionUrl,
getEditQuestionUrl: this.getEditQuestionUrl,
exercise_url: this.props.exercise_url,
is_md
}
console.log(this.props)
console.log(this.state)
@ -422,10 +419,10 @@ class ExerciseNewCommon extends Component {
`}</style>
{!this.state.editMode && <div className="padding20-30" style={{ background: '#fff' }}>
{!this.state.editMode && <div style={{ background: '#fff', height: 40, padding: '0 30px' }}>
<div className="displayTitle font-16">
<span>{exercise_name}</span>
<a className="fr mr6" onClick={() => { this.setState({ editMode: true }) }} style={{ lineHeight: '32px' }}>
<a className="fr mr6" onClick={() => { this.setState({ editMode: true }) }} style={{ lineHeight: '40px' }}>
<Tooltip title="编辑"><i className="iconfont icon-bianjidaibeijing font-20 color-green"></i></Tooltip>
</a>
</div>

@ -1,5 +1,5 @@
import React, { useState } from 'react'
import { Input, Form, Radio } from 'antd'
import { Input, Form, Radio, Button } from 'antd'
import './index.scss'
import { QuillForEditor } from 'educoder'
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor'
@ -90,8 +90,10 @@ export default ({ exercise_name = '', exercise_description, is_md, onSaveHandler
</Form.Item>
<Form.Item>
<a className="task-btn task-btn-orange fr mt4" onClick={onSave} >保存</a>
{isEdit && <a onClick={onCancel} className="defalutCancelbtn fr mt4">取消</a>}
<div className="btn-group">
<Button size='default' type='primary' style={{ marginRight: 12 }} onClick={onSave}>保存</Button>
<Button size='default' type='ghost' onClick={onCancel}>取消</Button>
</div>
</Form.Item>
</div>

@ -12,12 +12,8 @@
width: 70px;
}
.defalutCancelbtn {
height: '30px';
width: '70px';
font-size: '14px';
line-height: '30px';
margin-right: '16px';
.btn-group {
text-align: right;
}
.editor-tip {

@ -1,20 +1,12 @@
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Tooltip
} from 'antd';
import { Input, InputNumber, Select, Modal } from 'antd';
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios'
import update from 'immutability-helper'
import { qNameArray } from './common'
import {getUrl, ActionBtn, DMDEditor} from 'educoder';
const { TextArea } = Input;
const confirm = Modal.confirm;
const $ = window.$
const { Option } = Select;
import { ActionBtn} from 'educoder';
class ShixunEditor extends Component{
constructor(props){
@ -91,18 +83,12 @@ class ShixunEditor extends Component{
const {question_title, question_scores, question_type, shixun_name } = this.state;
const { question_id_to_insert_after, question_id
, shixun_id } = this.props
// TODO check
// if(!question_title) {
// this.props.showNotification('请先输入实训题完成要求'); return;
// }
for(let _i = 0; _i < question_scores.length; _i++) {
if (!question_scores[_i] || question_scores[_i] == '0') {
this.props.showNotification(`${_i+1}题的分值必须大于0`); return;
}
}
/**
{
"question_title":"社会主义核心价值观是?.",
@ -250,7 +236,6 @@ class ShixunEditor extends Component{
<Input value={shixun_name} onChange={(e) => this.setState({shixun_name: e.target.value})}
style={{ marginBottom: '10px'}}
></Input>
{/* <div style={{color: blackColor}} className="font-16 mb5">{shixun_name}</div> */}
<TPMMDEditor mdID={`question_${question_id}`} placeholder="请输入实训题完成要求" height={155}
initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
noStorage={true}

@ -1,28 +1,19 @@
import React,{ Component } from "react";
import React, { Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Tooltip
} from 'antd';
import { InputNumber, Tooltip } from 'antd';
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios'
import update from 'immutability-helper'
import {getUrl, ActionBtn, DMDEditor, ConditionToolTip} from 'educoder';
import { ActionBtn, DMDEditor, ConditionToolTip } from 'educoder';
import QuillForEditor from "../../../../common/quillForEditor";
const { TextArea } = Input;
const confirm = Modal.confirm;
const $ = window.$
const { Option } = Select;
const tagArray = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
]
class SingleEditor extends Component{
constructor(props){
class SingleEditor extends Component {
constructor(props) {
super(props);
/**
choice_id: 32076
@ -30,7 +21,7 @@ class SingleEditor extends Component{
choice_text: "1"
standard_boolean: true
*/
const {question_choices} = this.props;
const { question_choices } = this.props;
let _standard_answers = undefined;
let _question_choices = undefined;
if (question_choices) {
@ -48,7 +39,6 @@ class SingleEditor extends Component{
question_title: this.props.question_title || '',
question_type: this.props.question_type || 0,
question_score: this.props.question_score || this.props.init_question_score,
choice_editor: 'md',
quill_question_title: '',
quill_default_title: ''
}
@ -60,25 +50,25 @@ class SingleEditor extends Component{
this.setState({ question_choices, standard_answers })
}
deleteOption = (index) => {
let {question_choices}=this.state;
if(question_choices[index]===""){
let { question_choices } = this.state;
if (question_choices[index] === "") {
// repeat code
this.toMDMode(null)
this.setState(
(prevState) => ({
question_choices : update(prevState.question_choices, {$splice: [[index, 1]]}),
standard_answers : update(prevState.standard_answers, {$splice: [[index, 1]]})
question_choices: update(prevState.question_choices, { $splice: [[index, 1]] }),
standard_answers: update(prevState.standard_answers, { $splice: [[index, 1]] })
})
)
}else{
} else {
this.props.confirm({
content: `确认要删除这个选项吗?`,
onOk: () => {
this.toMDMode(null)
this.setState(
(prevState) => ({
question_choices : update(prevState.question_choices, {$splice: [[index, 1]]}),
standard_answers : update(prevState.standard_answers, {$splice: [[index, 1]]})
question_choices: update(prevState.question_choices, { $splice: [[index, 1]] }),
standard_answers: update(prevState.standard_answers, { $splice: [[index, 1]] })
})
)
}
@ -86,11 +76,11 @@ class SingleEditor extends Component{
}
}
onSave = () => {
const {question_title, question_score, question_type, question_choices, standard_answers } = this.state;
const { question_title, question_score, question_choices, standard_answers } = this.state;
const { question_id_to_insert_after, question_id } = this.props
// TODO check
const answerArray = standard_answers.map((item, index) => { return item == true ? index+1 : -1 }).filter(item => item != -1);
if(!question_title) {
const answerArray = standard_answers.map((item, index) => { return item == true ? index + 1 : -1 }).filter(item => item != -1);
if (!question_title) {
this.refs['titleEditor'].showError()
this.props.showNotification('题目:不能为空'); return;
@ -100,18 +90,18 @@ class SingleEditor extends Component{
const intScore = parseFloat(question_score)
if (intScore == 0) {
this.props.showNotification('分值必须大于0'); return;
} else if(!question_score || intScore == NaN) {
} else if (!question_score || intScore == NaN) {
this.props.showNotification('分值:不能为空'); return;
}
if(!answerArray || answerArray.length == 0) {
if (!answerArray || answerArray.length == 0) {
this.props.showNotification('请先点击选择本选择题的正确选项'); return;
}
if(!question_title) {
if (!question_title) {
this.refs['titleEditor'].showError()
this.props.showNotification('题目:不能为空'); return;
}
for(let i = 0; i < question_choices.length; i++) {
for (let i = 0; i < question_choices.length; i++) {
if (!question_choices[i]) {
this.refs[`optionEditor${i}`].showError()
this.props.showNotification(`请先输入 ${tagArray[i]} 选项的内容`); return;
@ -138,14 +128,14 @@ class SingleEditor extends Component{
}).then((response) => {
if (response.data.status == 0) {
this.props.addSuccess()
}else if(response.data.status == 3){
} else if (response.data.status == 3) {
// 已发布试卷编辑保存
this.props.changeScore(question_id,answerArray);
this.props.changeScore(question_id, answerArray);
}
})
.catch(function (error) {
console.log(error);
});
.catch(function (error) {
console.log(error);
});
} else {
const url = this.props.getAddQuestionUrl();
@ -174,13 +164,7 @@ class SingleEditor extends Component{
componentDidMount = () => {
}
onOptionClick = (index) => {
// if (this.props.exerciseIsPublish) {
// return;
// }
let standard_answers = this.state.standard_answers.slice(0)
standard_answers[index] = !standard_answers[index]
this.setState({ standard_answers })
@ -206,24 +190,6 @@ class SingleEditor extends Component{
this.mdReactObject = that;
}
toShowMode = () => {
}
// 切换编辑器
handleChangeEditor = (e) => {
const {quill_question_title} = this.state;
const value = e.target.value
if (value === 'quill') {
const _val = quill_question_title ? JSON.parse(quill_question_title) : '';
this.setState({
quill_default_title: _val
})
}
this.setState({
choice_editor: value
});
}
// quill编辑器内容变化时调用此接口
handleCtxChange = (ctx) => {
@ -235,37 +201,19 @@ class SingleEditor extends Component{
}
render() {
let { question_title, question_score, question_type, question_choices, standard_answers, choice_editor, quill_default_title } = this.state;
let { question_id, index, exerciseIsPublish,
// question_title,
// question_type,
// question_score,
isNew } = this.props;
// const { getFieldDecorator } = this.props.form;
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const isEdit = !!this.props.question_id
let { question_title, question_score, question_choices, standard_answers, quill_default_title } = this.state;
let { index, exerciseIsPublish, is_md } = this.props;
const qNumber = `question_${index}`;
// TODO show模式 isNew为false isEdit为false
// [true, false, true] -> [0, 2]
const answerTagArray = standard_answers.map((item, index) => { return item == true ? tagArray[index] : -1 }).filter(item => item != -1);
console.log("xuanzheshijuan");
console.log(answerTagArray);
console.log(!exerciseIsPublish);
return(
<div className="padding20-30 bor-bottom-greyE signleEditor" id={qNumber}>
<style>{`
return (
<div className="padding20-30 bor-bottom-greyE signleEditor" id={qNumber}>
<style>{`
.optionMdEditor {
flex:1
}
.optionRow {
margin:0px!important;
/* margin-bottom: 20px!important; */
}
.signleEditor .content_editorMd_show{
display: flex;
@ -277,8 +225,6 @@ class SingleEditor extends Component{
.editor_area{
display: inline-block;
float: right;
// line-height: 30px;
// height: 30px;
}
.editor_txt{
margin-right: 10px;
@ -290,103 +236,90 @@ class SingleEditor extends Component{
vertical: center;
}
`}</style>
<p className="mb10 clearfix">
{/* {!question_id ? '新建' : '编辑'} */}
<span className="color-blue font-16 mr20 fl">选择题</span>
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
{/* <Switch checkedChildren="MD" unCheckedChildren="Quill"></Switch> */}
{/* <div className="editor_area">
<span className="editor_txt">切换编辑器:</span>
<Radio.Group style={{ float: 'right' }} value={choice_editor} onChange={this.handleChangeEditor}>
<Radio className="radio_style" value={'md'}>MD</Radio>
<Radio className="radio_style" value={'quill'}>Quill</Radio>
</Radio.Group>
</div> */}
</p>
{choice_editor === 'md'
? <TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} className="mb20"
initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
ref="titleEditor"
></TPMMDEditor>
: <QuillForEditor
wrapStyle={{ marginBottom: '35px' }}
style={{ height: '109px' }}
options={['code', 'image', 'formula']}
placeholder="请您输入题目"
value={quill_default_title}
onContentChange={this.handleCtxChange}
></QuillForEditor>
}
{question_choices.map( (item, index) => {
const bg = standard_answers[index] ? 'check-option-bg' : ''
return <div className="df optionRow " >
{/* 点击设置答案 */}
{/* TODO 加了tooltip后会丢失掉span的class */}
{/* <Tooltip title={standard_answers[index] ? '点击取消标准答案设置' : '点击设置为标准答案'}> */}
<span class={`option-item fr mr10 color-grey select-choice ${bg} `}
name="option_span" onClick={() => this.onOptionClick(index)} style={{flex: '0 0 38px'}}>
<ConditionToolTip title={standard_answers[index] ? '点击取消标准答案设置' : '点击设置为标准答案'} placement="left" condition={true}>
<div style={{width: '100%', height: '100%'}}>{tagArray[index]}</div>
</ConditionToolTip>
</span>
{/* </Tooltip> */}
<div style={{ flex: '0 0 1038px'}}>
<DMDEditor
ref={`optionEditor${index}`}
toMDMode={this.toMDMode} toShowMode={this.toShowMode}
height={166} className={'optionMdEditor'} noStorage={true}
mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)}
initValue={item}
></DMDEditor>
</div>
{exerciseIsPublish || index===0?
<i className=" font-18 ml15 mr20"></i>
:<Tooltip title="删除">
<i className="iconfont icon-htmal5icon19 font-18 color-grey-c ml15" onClick={() => this.deleteOption(index)}></i>
</Tooltip> }
{ !exerciseIsPublish && <Tooltip title={`新增参考答案`}>
<i className="color-green font-16 iconfont icon-roundaddfill ml6"
onClick={() => this.addOption()}
style={{float: 'right', visibility: index == question_choices.length - 1 ? '' : 'hidden', marginTop: '2px'}}
></i>
</Tooltip>}
</div>
}) }
<p className="mb10 clearfix">
<span className="color-blue font-16 mr20 fl">选择题</span>
<span className="color-grey-9 font-12 fl">客观题由系统自动评分请设置标准答案</span>
</p>
{is_md ? <TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} className="mb20"
initValue={question_title} onChange={(val) => this.setState({ question_title: val })}
ref="titleEditor"
></TPMMDEditor>
: <QuillForEditor
wrapStyle={{ marginBottom: '35px' }}
style={{ height: '109px' }}
options={['code', 'image', 'formula']}
placeholder="请您输入题目"
value={quill_default_title}
onContentChange={this.handleCtxChange}
></QuillForEditor>
}
<div className="mb20">
<span
style={{color: '#FF6800'}}>{'温馨提示:点击选项输入框可设置答案;选中的选项即为正确答案,选择多个答案即为多选题'}</span>
{ answerTagArray && !!answerTagArray.length ?
<React.Fragment>
<span className="fr color-orange">{answerTagArray.join(' ')}</span>
<span className="fr">标准答案</span>
</React.Fragment>
:
""
}
{question_choices.map((item, index) => {
const bg = standard_answers[index] ? 'check-option-bg' : ''
return <div className="df optionRow " >
{/* 点击设置答案 */}
<span class={`option-item fr mr10 color-grey select-choice ${bg} `}
name="option_span" onClick={() => this.onOptionClick(index)} style={{ flex: '0 0 38px' }}>
<ConditionToolTip title={standard_answers[index] ? '点击取消标准答案设置' : '点击设置为标准答案'} placement="left" condition={true}>
<div style={{ width: '100%', height: '100%' }}>{tagArray[index]}</div>
</ConditionToolTip>
</span>
<div style={{ flex: '0 0 1038px' }}>
<DMDEditor
ref={`optionEditor${index}`}
toMDMode={this.toMDMode} toShowMode={this.toShowMode}
height={166} className={'optionMdEditor'} noStorage={true}
mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)}
initValue={item}
></DMDEditor>
</div>
{exerciseIsPublish || index === 0 ?
<i className=" font-18 ml15 mr20"></i>
: <Tooltip title="删除">
<i className="iconfont icon-htmal5icon19 font-18 color-grey-c ml15" onClick={() => this.deleteOption(index)}></i>
</Tooltip>}
{!exerciseIsPublish && <Tooltip title={`新增参考答案`}>
<i className="color-green font-16 iconfont icon-roundaddfill ml6"
onClick={() => this.addOption()}
style={{ float: 'right', visibility: index == question_choices.length - 1 ? '' : 'hidden', marginTop: '2px' }}
></i>
</Tooltip>}
</div>
})}
<div className="mb20">
<span
style={{ color: '#FF6800' }}>{'温馨提示:点击选项输入框可设置答案;选中的选项即为正确答案,选择多个答案即为多选题'}</span>
{answerTagArray && !!answerTagArray.length ?
<React.Fragment>
<span className="fr color-orange">{answerTagArray.join(' ')}</span>
<span className="fr">标准答案</span>
</React.Fragment>
:
""
}
</div>
<div>
分值
<InputNumber step={0.1} precision={1} min={0} max={100} style={{width: 100}} value={question_score} onChange={this.on_question_score_change}
disabled={exerciseIsPublish}
></InputNumber>&nbsp;
<div>
分值
<InputNumber step={0.1} precision={1} min={0} max={100} style={{ width: 100 }} value={question_score} onChange={this.on_question_score_change}
disabled={exerciseIsPublish}
></InputNumber>&nbsp;
<span className="fr">
<ActionBtn style="greyBack" className="middle mr20" onClick={this.onCancel}
>取消</ActionBtn>
<ActionBtn style="blue" className="middle" onClick={this.onSave}>保存</ActionBtn>
</span>
</div>
<ActionBtn style="greyBack" className="middle mr20" onClick={this.onCancel}
>取消</ActionBtn>
<ActionBtn style="blue" className="middle" onClick={this.onSave}>保存</ActionBtn>
</span>
</div>
)
}
</div>
)
}
}
// RouteHOC()
export default (SingleEditor);

Loading…
Cancel
Save