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 :require_login
before_action :certi_identity_auth, only: [:create, :edit, :update, :destroy, :set_public, :revoke_item, :cancel_items] 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 :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] before_action :identity_auth, only: [:index]
def index def index
@ -52,7 +52,7 @@ class ExaminationBanksController < ApplicationController
end end
def destroy 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 ActiveRecord::Base.transaction do
ApplyAction.where(container_type: "ExaminationBank", container_id: @exam.id).destroy_all ApplyAction.where(container_type: "ExaminationBank", container_id: @exam.id).destroy_all
@exam.destroy! @exam.destroy!

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

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

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

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

@ -75,12 +75,8 @@ class ExerciseNewCommon extends Component {
axios.get(url) axios.get(url)
.then((response) => { .then((response) => {
if (response.data.exercise) { 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({ this.setState({
...exercise, ...response.data,
...others,
editMode: false editMode: false
}) })
this.props.initData && this.props.initData(response.data) this.props.initData && this.props.initData(response.data)
@ -397,6 +393,7 @@ class ExerciseNewCommon extends Component {
getAddQuestionUrl: this.getAddQuestionUrl, getAddQuestionUrl: this.getAddQuestionUrl,
getEditQuestionUrl: this.getEditQuestionUrl, getEditQuestionUrl: this.getEditQuestionUrl,
exercise_url: this.props.exercise_url, exercise_url: this.props.exercise_url,
is_md
} }
console.log(this.props) console.log(this.props)
console.log(this.state) console.log(this.state)
@ -422,10 +419,10 @@ class ExerciseNewCommon extends Component {
`}</style> `}</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"> <div className="displayTitle font-16">
<span>{exercise_name}</span> <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> <Tooltip title="编辑"><i className="iconfont icon-bianjidaibeijing font-20 color-green"></i></Tooltip>
</a> </a>
</div> </div>

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

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

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

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

Loading…
Cancel
Save