dev_jupyter
杨树林 5 years ago
commit 20061430e0

@ -0,0 +1,38 @@
/*
* @Description: 填空
* @Author: tangjiang
* @Github:
* @Date: 2020-01-06 09:02:29
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-06 16:04:46
*/
import Quill from 'quill';
let Inline = Quill.import('blots/inline');
class FillBlot extends Inline {
static create (value) {
const node = super.cerate();
node.classList.add('icon icon-bianji2');
node.setAttribute('data-fill', 'fill');
node.addEventListener('DOMNodeRemoved', function () {
alert(123);
}, false);
return node;
}
static value (node) {
return {
dataSet: node.getAttribute('data-fill'),
onDOMNodeRemoved: () => {
alert('123456');
}
}
}
}
FillBlot.blotName = "fill";
FillBlot.tagName = "span";
export default FillBlot;

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-12-18 08:49:30 * @Date: 2019-12-18 08:49:30
* @LastEditors : tangjiang * @LastEditors : tangjiang
* @LastEditTime : 2019-12-31 15:11:37 * @LastEditTime : 2020-01-06 16:45:50
*/ */
import './index.scss'; import './index.scss';
import 'quill/dist/quill.core.css'; // 核心样式 import 'quill/dist/quill.core.css'; // 核心样式
@ -18,8 +18,11 @@ 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 Toolbar from 'quill/modules/toolbar';
import FillBlot from './FillBlot';
const Size = Quill.import('attributors/style/size'); const Size = Quill.import('attributors/style/size');
const Font = Quill.import('formats/font'); const Font = Quill.import('formats/font');
// const Color = Quill.import('attributes/style/color'); // const Color = Quill.import('attributes/style/color');
Size.whitelist = ['12px', '14px', '16px', '18px', '20px', false]; Size.whitelist = ['12px', '14px', '16px', '18px', '20px', false];
Font.whitelist = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif']; Font.whitelist = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif'];
@ -29,6 +32,8 @@ window.katex = katex;
Quill.register(ImageBlot); Quill.register(ImageBlot);
Quill.register(Size); Quill.register(Size);
Quill.register(Font, true); Quill.register(Font, true);
// Quill.register({'modules/toolbar': Toolbar});
Quill.register(FillBlot);
// Quill.register(Color); // Quill.register(Color);
function QuillForEditor ({ function QuillForEditor ({
@ -42,6 +47,7 @@ function QuillForEditor ({
wrapStyle = {}, wrapStyle = {},
showUploadImage, showUploadImage,
onContentChange, onContentChange,
// addFill, // 点击填空成功的回调
// getQuillContent // getQuillContent
}) { }) {
// toolbar 默认值 // toolbar 默认值
@ -62,6 +68,8 @@ function QuillForEditor ({
// quill 实例 // quill 实例
const [quill, setQuill] = useState(null); const [quill, setQuill] = useState(null);
const [selection, setSelection] = useState(null); const [selection, setSelection] = useState(null);
const [fillCount, setFillCount] = useState(0);
const [quillCtx, setQuillCtx] = useState({});
// 文本内容变化时 // 文本内容变化时
const handleOnChange = content => { const handleOnChange = content => {
@ -70,18 +78,23 @@ function QuillForEditor ({
}; };
const renderOptions = options || defaultConfig; const renderOptions = options || defaultConfig;
// quill 配置信息 // quill 配置信息
const quillOption = { const quillOption = {
modules: { modules: {
toolbar: renderOptions toolbar: renderOptions
// toolbar: {
// container: renderOptions
// }
}, },
readOnly, readOnly,
placeholder, placeholder,
theme: readOnly ? 'bubble' : 'snow' theme: readOnly ? 'bubble' : 'snow',
}; };
useEffect(() => { useEffect(() => {
const quillNode = document.createElement('div'); const quillNode = document.createElement('div');
editorRef.current.appendChild(quillNode); editorRef.current.appendChild(quillNode);
const _quill = new Quill(editorRef.current, quillOption); const _quill = new Quill(editorRef.current, quillOption);
@ -121,13 +134,20 @@ function QuillForEditor ({
} }
} }
}); });
_quill.getModule('toolbar').addHandler('fill', (e) => {
setFillCount(fillCount + 1);
const range = _quill.getSelection(true);
_quill.insertText(range.index, '▁');
// 点击填空图标时,插入一个下划线
// 1. 获取编辑器内容
});
}, []); }, []);
// 设置值 // 设置值
useEffect(() => { useEffect(() => {
if (!quill) return if (!quill) return
// debugger;
const previous = quill.getContents() const previous = quill.getContents()
if (value && value.hasOwnProperty('ops')) { if (value && value.hasOwnProperty('ops')) {
@ -173,7 +193,36 @@ function QuillForEditor ({
let handler; let handler;
quill.on( quill.on(
'text-change', 'text-change',
(handler = () => { (handler = (delta, oldDelta, source) => {
// let del = false;
// let delLen = 1;
// delta.ops.forEach(o => {
// // 存在删除并且只删除一个
// if (o.delete) {
// del = true;
// }
// });
// 删除编辑器内容
// if (del) {
// delLen = delta.ops[0].retain || 1; // 删除数组的长度
// // 获取删除的内容并判断其它是否有填空内容
// console.log('原编辑器内容', oldDelta);
// console.log('编辑器内容', quillCtx);
// }
// 获取删除的内容
// oldDelta
// if (del) {
// const ops = oldDelta.ops;
// const len = ops.length;
// // if (ops[len - 1] && ops[len - 1].insert) {
// // const str = ops[len - 1].insert;
// // const _len = str.length;
// // const _last = str.substr(_len - 1);
// // console.log('删除的一项', _last);
// // }
// }
const _ctx = quill.getContents();
setQuillCtx(_ctx);
handleOnChange(quill.getContents()); // getContents: 检索编辑器内容 handleOnChange(quill.getContents()); // getContents: 检索编辑器内容
}) })
); );

@ -110,4 +110,21 @@
content: "微软雅黑"; content: "微软雅黑";
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
} }
//
.ql-snow .ql-fill{
display: inline-block;
position: relative;
color: #05101A;
// font-size: 18px;
vertical-align: top;
&::before{
position: absolute;
left: 50%;
top: -1px;
content: '\e709';
font-family: 'iconfont';
margin-left: -7px;
}
}
} }

@ -213,7 +213,7 @@ const NewOrEditTask = (props) => {
{ renderQuit() } { renderQuit() }
</div> </div>
<div className="split-pane-area"> <div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%"> <SplitPane className='outer-split-pane' split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<div className={'split-pane-left'}> <div className={'split-pane-left'}>
<LeftPane /> <LeftPane />
</div> </div>

@ -0,0 +1,69 @@
<!--
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-06 16:20:03
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-06 16:46:44
-->
## QuillForEditor 使用 [https://quilljs.com/]
### 导入
- 目录 src/common/quillForEditor (默认加载当前文件夹下的 index.js 文件)
### 参数
| 字段 | 描述 |
| ----- | ----- |
| placeholder | 提示信息 |
| readOnly | 只读(只读取模式时,没有 工具栏且内容不可编辑通过用于展示quill内容) |
| autoFocus | 自动获得焦点 |
| options | 配置参数, 指定工具栏内容 |
| value | 文本编辑器内容 |
| imgAttrs | 指定上传图片的尺寸 |
| style | 指定quill容器样式 |
| wrapStyle | 指定包裹quill容器的样式|
| onContentChange | 当编辑器内容变化时调用此回调函数 |
| showUploadImage | 点击放大上传成功后的图片, 返回上传成功后的图片 url, (评论时点击图片这么大)|
### 添加工具栏
- 默认所有的
```
const options = [
'bold', // 加粗
'italic', // 斜体
'underline', // 下划线
{size: ['12px', '14px', '16px', '18px', '20px']}, // 字体大小
{align: []}, // 对齐方式
{list: 'ordered'}, // 有序列表
{list: 'bullet'}, // 无序列表
{script: 'sub'}, // 下标 x2
{script: 'super'}, // 上标 平方 (x2)
{ 'color': [] }, // 字体颜色
{ 'background': [] }, // 背景色
{header: [1,2,3,4,5,false]}, // H1,H2 ...
'blockquote', // 文件左边加一个边框样式
'code-block', // 块内容
'link', // 链接
'image', // 图片
'video', // 视频
'formula', // 数学公式
'clean' // 清除
]
```
### 使用
````
import QuillForEditor from 'xxx';
<QuillForEditor>
</QuillForEditor>
````

@ -1,14 +1,10 @@
/* /*
* @Description: * @Description:
* @Author: tangjiang * @Author: tangjiang
* @Github: * @Github:
* @Date: 2019-11-20 10:35:40 * @Date: 2019-11-20 10:35:40
* @LastEditors : tangjiang * @LastEditors : tangjiang
<<<<<<< HEAD * @LastEditTime : 2020-01-06 16:17:22
* @LastEditTime : 2020-01-03 17:35:45
=======
* @LastEditTime : 2020-01-03 19:02:17
>>>>>>> dev_aliyun
*/ */
import './index.scss'; import './index.scss';
// import 'katex/dist/katex.css'; // import 'katex/dist/katex.css';
@ -79,7 +75,7 @@ class EditTab extends React.Component {
this.setState({ this.setState({
scrollEl: oWrap, scrollEl: oWrap,
targetEl: oTarget, targetEl: oTarget,
offsetTop: offsetTop, // 记录初始位置 offsetTop: offsetTop, // 记录初始位置
scrollHeight, scrollHeight,
}, () => { }, () => {
this.state.scrollEl.addEventListener('scroll', this.handleScroll, false); this.state.scrollEl.addEventListener('scroll', this.handleScroll, false);
@ -93,7 +89,7 @@ class EditTab extends React.Component {
// componentDidUpdate (nextProp) { // componentDidUpdate (nextProp) {
// console.log(nextProp); // console.log(nextProp);
// } // }
componentWillUnmount (nextPro) { componentWillUnmount (nextPro) {
this.state.scrollEl.removeEventListener('scroll', this.handleScroll, false); this.state.scrollEl.removeEventListener('scroll', this.handleScroll, false);
} }
@ -116,7 +112,7 @@ class EditTab extends React.Component {
} }
} }
// 改变任务名称 // 改变任务名称
handleNameChange = (e) => { handleNameChange = (e) => {
const value = e.target.value; const value = e.target.value;
this.props.validateOJName(value); this.props.validateOJName(value);
@ -171,8 +167,8 @@ class EditTab extends React.Component {
} }
render () { render () {
const { const {
ojForm, ojForm,
ojFormValidate, ojFormValidate,
// testCases = [], // 测试用例集合 // testCases = [], // 测试用例集合
addTestCase, // 添加测试用例 addTestCase, // 添加测试用例
@ -183,10 +179,9 @@ class EditTab extends React.Component {
tag_discipline_id, tag_discipline_id,
knowledges knowledges
} = this.props; } = this.props;
// console.log('knowledge======>>>>>>', knowledges); console.log('knowledge======>>>>>>', knowledges);
// const {knowledges} = this.state; // const {knowledges} = this.state;
// 表单label // 表单label
// console.log('props=====>>>>', this.props);
const myLabel = (name, subTitle, nostar) => { const myLabel = (name, subTitle, nostar) => {
if (subTitle) { if (subTitle) {
return ( return (
@ -225,8 +220,8 @@ class EditTab extends React.Component {
return <AddTestDemo return <AddTestDemo
key={`${i}`} key={`${i}`}
isOpen={openTestCodeIndex.includes(i)} isOpen={openTestCodeIndex.includes(i)}
onSubmitTest={handleSubmitTest} onSubmitTest={handleSubmitTest}
onDeleteTest={handleDeleteTest} onDeleteTest={handleDeleteTest}
testCase={item} testCase={item}
testCaseValidate={testCasesValidate[i]} testCaseValidate={testCasesValidate[i]}
index={i} index={i}
@ -282,10 +277,10 @@ class EditTab extends React.Component {
const quillConfig = [ const quillConfig = [
{ header: 1}, {header: 2}, { header: 1}, {header: 2},
// {size: ['12px', '14px', '16px', '18px', '20px', false]}, // {size: ['12px', '14px', '16px', '18px', '20px', false]},
'bold', 'italic', 'underline', 'strike', // 切换按钮 'bold', 'italic', 'underline', 'strike', // 切换按钮
'blockquote', 'code-block', // 代码块 'blockquote', 'code-block', // 代码块
{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表 {align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表
{ 'script': 'sub'}, { 'script': 'super' }, { 'script': 'sub'}, { 'script': 'super' },
{ 'color': [] }, { 'background': [] }, // 字体颜色与背景色 { 'color': [] }, { 'background': [] }, // 字体颜色与背景色
// {font: []}, // {font: []},
'image', 'formula', // 数学公式、图片、视频 'image', 'formula', // 数学公式、图片、视频
@ -310,7 +305,7 @@ class EditTab extends React.Component {
}); });
} }
loop(arrs, tempArr); loop(arrs, tempArr);
// 获取选中的下拉值 // 获取选中的下拉值
let choid_ids = []; let choid_ids = [];
// let tempKnowledges = []; // let tempKnowledges = [];
@ -325,10 +320,10 @@ class EditTab extends React.Component {
}); });
} }
}); });
console.log(choid_ids); console.log(choid_ids);
return ( return (
<Cascader <Cascader
placeholder="请选择" placeholder="请选择"
options={tempArr} options={tempArr}
expandTrigger="hover" expandTrigger="hover"
@ -338,7 +333,7 @@ class EditTab extends React.Component {
/> />
) )
} }
// 知识点 // 知识点
const handleKnowledgeChange = (values= []) => { const handleKnowledgeChange = (values= []) => {
const _result = []; const _result = [];
@ -349,11 +344,11 @@ class EditTab extends React.Component {
// 保存选择的知识点 // 保存选择的知识点
this.props.saveTagDisciplineId(_result); this.props.saveTagDisciplineId(_result);
} }
return ( return (
<div className={'editor_area'} id="textCase"> <div className={'editor_area'} id="textCase">
<Form className={'editor_form'}> <Form className={'editor_form'}>
<FormItem <FormItem
className={`input_area flex_50 flex_50_left`} className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['difficult'])}</span>} label={<span>{myLabel(jcLabel['difficult'])}</span>}
validateStatus={ojFormValidate.difficult.validateStatus} validateStatus={ojFormValidate.difficult.validateStatus}
@ -365,7 +360,7 @@ class EditTab extends React.Component {
</Select> </Select>
</FormItem> </FormItem>
<FormItem <FormItem
className={`input_area flex_50 flex_50_right`} className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['sub_discipline_id'], '合理的课程分类有利于快速检索')}</span>} label={<span>{myLabel(jcLabel['sub_discipline_id'], '合理的课程分类有利于快速检索')}</span>}
validateStatus={ojFormValidate.sub_discipline_id.validateStatus} validateStatus={ojFormValidate.sub_discipline_id.validateStatus}
@ -375,7 +370,7 @@ class EditTab extends React.Component {
{/* <Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}> {/* <Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
{getOptions('category')} {getOptions('category')}
</Select> */} </Select> */}
{/* <Cascader {/* <Cascader
options={courseQuestions} options={courseQuestions}
expandTrigger="hover" expandTrigger="hover"
onChange={this.handleChangeCategory} onChange={this.handleChangeCategory}
@ -388,14 +383,14 @@ class EditTab extends React.Component {
className='input_area flex_100' className='input_area flex_100'
label={<span>{myLabel(jcLabel['knowledge'], '', 'nostar')}</span>} label={<span>{myLabel(jcLabel['knowledge'], '', 'nostar')}</span>}
> >
<KnowLedge <KnowLedge
options={knowledges} options={knowledges}
values={tag_discipline_id} values={tag_discipline_id}
onChange={handleKnowledgeChange} onChange={handleKnowledgeChange}
/> />
</FormItem> </FormItem>
<FormItem <FormItem
className={`input_area flex_50 flex_50_left`} className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>} label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
validateStatus={ojFormValidate.timeLimit.validateStatus} validateStatus={ojFormValidate.timeLimit.validateStatus}
@ -405,7 +400,7 @@ class EditTab extends React.Component {
<InputNumber value={ojForm.timeLimit} min={0} max={5} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/> <InputNumber value={ojForm.timeLimit} min={0} max={5} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/>
</FormItem> </FormItem>
<FormItem <FormItem
className={`input_area flex_50 flex_50_right`} className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['language'])}</span>} label={<span>{myLabel(jcLabel['language'])}</span>}
validateStatus={ojFormValidate.language.validateStatus} validateStatus={ojFormValidate.language.validateStatus}
@ -417,7 +412,7 @@ class EditTab extends React.Component {
</Select> </Select>
</FormItem> </FormItem>
<FormItem <FormItem
className={`input_area flex_100`} className={`input_area flex_100`}
label={<span>{myLabel(jcLabel['name'])}</span>} label={<span>{myLabel(jcLabel['name'])}</span>}
validateStatus={ojFormValidate.name.validateStatus} validateStatus={ojFormValidate.name.validateStatus}
@ -432,24 +427,24 @@ class EditTab extends React.Component {
onChange={this.handleNameChange} onChange={this.handleNameChange}
/> />
</FormItem> </FormItem>
<FormItem <FormItem
className={`input_area flex_100`} className={`input_area flex_100`}
label={<span>{myLabel(jcLabel['description'])}</span>} label={<span>{myLabel(jcLabel['description'])}</span>}
validateStatus={ojFormValidate.description.validateStatus} validateStatus={ojFormValidate.description.validateStatus}
help={ojFormValidate.description.errMsg} help={ojFormValidate.description.errMsg}
colon={ false } colon={ false }
> >
<QuillForEditor <QuillForEditor
style={{ height: '200px' }} style={{ height: '200px' }}
placeholder="请输入描述信息" placeholder="请输入描述信息"
onContentChange={handleContentChange} onContentChange={handleContentChange}
options={quillConfig} options={quillConfig}
value={ojForm.description} value={ojForm.description}
/> />
</FormItem> </FormItem>
{/* <FormItem {/* <FormItem
className={`input_area flex_50 flex_50_right`} className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>} label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>}
validateStatus={ojFormValidate.openOrNot.validateStatus} validateStatus={ojFormValidate.openOrNot.validateStatus}
@ -460,9 +455,9 @@ class EditTab extends React.Component {
{getOptions('openOrNot')} {getOptions('openOrNot')}
</Select> </Select>
</FormItem> */} </FormItem> */}
</Form> </Form>
{/* 添加测试用例 */} {/* 添加测试用例 */}
<div className={'test_demo_title'} ref={this.headerRef}> <div className={'test_demo_title'} ref={this.headerRef}>
<h2>测试用例</h2> <h2>测试用例</h2>
@ -481,11 +476,11 @@ class EditTab extends React.Component {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const ojFormReducer = state.ojFormReducer; const ojFormReducer = state.ojFormReducer;
const { const {
ojForm, ojForm,
position, position,
testCases, testCases,
openTestCodeIndex, openTestCodeIndex,
testCasesValidate, testCasesValidate,
ojFormValidate, ojFormValidate,
courseQuestions, courseQuestions,
tag_discipline_id, tag_discipline_id,

@ -217,3 +217,42 @@
.Resizer.disabled:hover { .Resizer.disabled:hover {
border-color: transparent; border-color: transparent;
} }
.outer-split-pane{
& > .Resizer{
position: relative;
&::before,
&::after{
position: absolute;
right: -12px;
top: 50%;
transition: opacity, background .3s;
}
&::before{
content: '';
border-radius: 50%;
background: rgba(235,235,235,.3);
width: 24px;
height: 24px;
// font-size: 12px;
}
&::after{
content: '\e712';
font-family: 'iconfont';
transform: scale(.7);
color: #666;
margin-top: -2px;
right: -14px;
opacity: .3;
}
&:hover{
&::before{
background: rgba(235,235,235, 1);
}
&::after{
opacity: 1;
}
}
}
}

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-23 10:53:19 * @Date: 2019-11-23 10:53:19
* @LastEditors : tangjiang * @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 16:22:47 * @LastEditTime : 2020-01-06 15:27:34
*/ */
import './index.scss'; import './index.scss';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
@ -144,7 +144,7 @@ function StudentStudy (props) {
</div> </div>
</div> </div>
<div className="split-pane-area"> <div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%"> <SplitPane className="outer-split-pane" split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<div className={'split-pane-left'}> <div className={'split-pane-left'}>
<LeftPane /> <LeftPane />
</div> </div>

@ -303,18 +303,18 @@ class SingleEditor extends Component{
<span className="xingtigan fl">题干</span> <span className="xingtigan fl">题干</span>
</p> </p>
{/*<TPMMDEditor mdID={qNumber} placeholder="请您输入题干" height={155} className=" mt10"*/} <TPMMDEditor mdID={qNumber} placeholder="请您输入题干" height={155} className=" mt10"
{/* initValue={question_title} onChange={(val) => this.setState({ question_title: val})}*/} initValue={question_title} onChange={(val) => this.setState({ question_title: val})}
{/* ref="titleEditor"*/} ref="titleEditor"
{/*></TPMMDEditor>*/} ></TPMMDEditor>
<QuillForEditor {/*<QuillForEditor*/}
style={{ height: '155px'}} {/* style={{ height: '155px'}}*/}
placeholder="请您输入题干" {/* placeholder="请您输入题干"*/}
value={question_title} {/* value={question_title}*/}
options={['code-block', 'image', 'formula']} {/* options={['code-block', 'image', 'formula']}*/}
/> {/*/>*/}
<div className="mb10 sortinxdirection"> <div className="mb10 sortinxdirection">
{/* {!question_id ? '新建' : '编辑'} */} {/* {!question_id ? '新建' : '编辑'} */}

Loading…
Cancel
Save