parent
b05c23df75
commit
eb02258a54
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description: 重写图片
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-16 15:50:45
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-17 16:44:48
|
|
||||||
*/
|
|
||||||
import Quill from "quill";
|
|
||||||
|
|
||||||
const BlockEmbed = Quill.import('blots/block/embed');
|
|
||||||
|
|
||||||
export default class ImageBlot extends BlockEmbed {
|
|
||||||
|
|
||||||
static create(value) {
|
|
||||||
|
|
||||||
const node = super.create();
|
|
||||||
|
|
||||||
node.setAttribute('alt', value.alt);
|
|
||||||
node.setAttribute('src', value.url);
|
|
||||||
|
|
||||||
if (value.width) {
|
|
||||||
node.setAttribute('width', value.width);
|
|
||||||
}
|
|
||||||
if (value.height) {
|
|
||||||
node.setAttribute('height', value.height);
|
|
||||||
}
|
|
||||||
// 宽度和高度都不存在时,
|
|
||||||
if (!value.width && !value.height) {
|
|
||||||
node.setAttribute('display', 'block');
|
|
||||||
node.setAttribute('width', '100%');
|
|
||||||
}
|
|
||||||
// 给图片添加点击事件
|
|
||||||
node.onclick = () => {
|
|
||||||
value.onClick && value.onClick(value.url);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static value (node) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
alt: node.getAttribute('alt'),
|
|
||||||
url: node.getAttribute('src'),
|
|
||||||
onclick: node.onclick,
|
|
||||||
// width: node.width,
|
|
||||||
// height: node.height,
|
|
||||||
display: node.getAttribute('display')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageBlot.blotName = 'image';
|
|
||||||
ImageBlot.tagName = 'img';
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description:
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:09:42
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-18 08:46:20
|
|
||||||
*/
|
|
||||||
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 React, { useState, useReducer, useEffect } from 'react';
|
|
||||||
import useQuill from './useQuill';
|
|
||||||
|
|
||||||
function ReactQuill ({
|
|
||||||
disallowColors, // 不可见时颜色
|
|
||||||
placeholder, // 提示信息
|
|
||||||
uploadImage, // 图片上传
|
|
||||||
onChange, // 内容变化时
|
|
||||||
options, // 配置信息
|
|
||||||
value, // 显示的内容
|
|
||||||
style,
|
|
||||||
showUploadImage // 显示上传图片
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [element, setElement] = useState(); // quill 渲染节点
|
|
||||||
|
|
||||||
useQuill({
|
|
||||||
disallowColors,
|
|
||||||
placeholder,
|
|
||||||
uploadImage,
|
|
||||||
onChange,
|
|
||||||
options,
|
|
||||||
value,
|
|
||||||
showUploadImage,
|
|
||||||
element
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='react_quill_area' ref={setElement} style={style}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ReactQuill;
|
|
@ -1,47 +0,0 @@
|
|||||||
function deepEqual (prev, current) {
|
|
||||||
if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!prev && current)
|
|
||||||
|| (prev && !current)
|
|
||||||
|| (!prev && !current)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(prev)) {
|
|
||||||
if (!Array.isArray(current)) return false;
|
|
||||||
if (prev.length !== current.length) return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < prev.length; i++) {
|
|
||||||
if (!deepEqual(current[i], prev[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof current === 'object') {
|
|
||||||
if (typeof prev !== 'object') return false;
|
|
||||||
const prevKeys = Object.keys(prev);
|
|
||||||
const curKeys = Object.keys(current);
|
|
||||||
|
|
||||||
if (prevKeys.length !== curKeys.length) return false;
|
|
||||||
|
|
||||||
prevKeys.sort();
|
|
||||||
curKeys.sort();
|
|
||||||
|
|
||||||
for (let i = 0; i < prevKeys.length; i++) {
|
|
||||||
if (prevKeys[i] !== curKeys[i]) return false;
|
|
||||||
const key = prevKeys[i];
|
|
||||||
if (!deepEqual(prev[key], current[key])) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default deepEqual;
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description: 将多维数组转变成一维数组
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:35:01
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-16 11:36:22
|
|
||||||
*/
|
|
||||||
function flatten (array) {
|
|
||||||
return flatten.rec(array, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
flatten.rec = function flatten (array, result) {
|
|
||||||
|
|
||||||
for (let item of array) {
|
|
||||||
if (Array.isArray(item)) {
|
|
||||||
flatten(item, result);
|
|
||||||
} else {
|
|
||||||
result.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default flatten;
|
|
@ -1,32 +0,0 @@
|
|||||||
#quill-toolbar{
|
|
||||||
.quill-btn{
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.quill_image{
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 28px;
|
|
||||||
height: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
.image_input{
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.ql-image{
|
|
||||||
position: relative;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.react_quill_area{
|
|
||||||
.ql-toolbar:not(:last-child) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description: 导出 ReactQuill
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:08:24
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-16 11:37:13
|
|
||||||
*/
|
|
||||||
import ReactQuill from './ReactQuill';
|
|
||||||
import useQuill from './useQuill';
|
|
||||||
|
|
||||||
export default ReactQuill;
|
|
||||||
export { useQuill };
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description:
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-12 19:48:55
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-16 11:38:16
|
|
||||||
*/
|
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import deepEqual from './deepEqual';
|
|
||||||
|
|
||||||
function useDeepEqual (input) {
|
|
||||||
|
|
||||||
const [value, setValue] = useState(input);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
if (!deepEqual(input, value)) {
|
|
||||||
setValue(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [input, value]);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useDeepEqual;
|
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description: 创建 reactQuill实例
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:31:42
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-17 20:42:05
|
|
||||||
*/
|
|
||||||
import Quill from 'quill'; // 导入quill
|
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
|
||||||
import flatten from './flatten.js';
|
|
||||||
import useDeepEqualMemo from './useDeepEqualMemo';
|
|
||||||
import Katex from 'katex';
|
|
||||||
import ImageBlot from './ImageBlot';
|
|
||||||
import { fetchUploadImage } from '../../services/ojService.js';
|
|
||||||
import { getImageUrl } from 'educoder'
|
|
||||||
window.katex = Katex;
|
|
||||||
|
|
||||||
Quill.register(ImageBlot);
|
|
||||||
|
|
||||||
function useMountQuill ({
|
|
||||||
element,
|
|
||||||
options: passedOptions,
|
|
||||||
uploadImage,
|
|
||||||
showUploadImage,
|
|
||||||
imgAttrs = {} // 指定图片的宽高属性
|
|
||||||
}) {
|
|
||||||
|
|
||||||
// 是否引入 katex
|
|
||||||
const [katexLoaded, setKatexLoaded] = useState(Boolean(window.katex))
|
|
||||||
const [quill, setQuill] = useState(null);
|
|
||||||
|
|
||||||
const options = useDeepEqualMemo(passedOptions);
|
|
||||||
console.log('use mount quill: ', passedOptions);
|
|
||||||
|
|
||||||
// 判断options中是否包含公式
|
|
||||||
const requireKatex = useMemo(() => {
|
|
||||||
return flatten(options.modules.toolbar).includes('formula');
|
|
||||||
}, [options]);
|
|
||||||
|
|
||||||
// 加载katex
|
|
||||||
useEffect(() => {
|
|
||||||
if (!requireKatex) return;
|
|
||||||
if (katexLoaded) return;
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
if (window.katex) {
|
|
||||||
setKatexLoaded(true);
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => { // 定义回调清除定时器
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [
|
|
||||||
setKatexLoaded,
|
|
||||||
katexLoaded,
|
|
||||||
requireKatex
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 加载 quill
|
|
||||||
useEffect(() => {
|
|
||||||
if (!element) return;
|
|
||||||
if (requireKatex && !katexLoaded) {
|
|
||||||
element.innerHTML = `
|
|
||||||
<div style="color: #ddd">
|
|
||||||
Loading Katex...
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
// 清空内容
|
|
||||||
element.innerHTML = '';
|
|
||||||
console.log(element);
|
|
||||||
// 创建 quill 节点
|
|
||||||
const quillNode = document.createElement('div');
|
|
||||||
element.appendChild(quillNode); // 将quill节点追回到 element 元素中
|
|
||||||
|
|
||||||
const quill = new Quill(element, options);
|
|
||||||
setQuill(quill);
|
|
||||||
// 加载上传图片功能
|
|
||||||
if (typeof uploadImage === 'function') {
|
|
||||||
quill.getModule('toolbar').addHandler('image', (e) => {
|
|
||||||
// 创建type类型输入框加载本地图片
|
|
||||||
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 reader = new FileReader();
|
|
||||||
// reader.readAsDataURL(file);
|
|
||||||
// console.log('文件信息===>>', reader);
|
|
||||||
// reader.onload = function (e) {
|
|
||||||
// debugger;
|
|
||||||
// console.log('文件信息===>>', e.target.result);
|
|
||||||
// const image = new Image();
|
|
||||||
// image.src = e.target.result;
|
|
||||||
|
|
||||||
// image.onload = function () {
|
|
||||||
// // file.width =
|
|
||||||
// console.log(image.width, image.height);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const range = quill.getSelection(true);
|
|
||||||
let fileUrl = ''; // 保存上传成功后图片的url
|
|
||||||
// 上传文件
|
|
||||||
const result = await fetchUploadImage(formData);
|
|
||||||
// 获取上传图片的url
|
|
||||||
if (result.data && result.data.id) {
|
|
||||||
fileUrl = getImageUrl(`api/attachments/${result.data.id}`);
|
|
||||||
}
|
|
||||||
// 根据id获取文件路径
|
|
||||||
const { width, height } = imgAttrs;
|
|
||||||
// console.log('上传图片的url:', fileUrl);
|
|
||||||
if (fileUrl) {
|
|
||||||
quill.insertEmbed(range.index, 'image', {
|
|
||||||
url: fileUrl,
|
|
||||||
alt: '',
|
|
||||||
onClick: showUploadImage,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
element.innerHTML = '';
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
element,
|
|
||||||
options,
|
|
||||||
requireKatex,
|
|
||||||
katexLoaded,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return quill;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useMountQuill;
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description:
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:09:50
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-17 15:46:50
|
|
||||||
*/
|
|
||||||
import useQuillPlaceholder from './useQuillPlaceholder';
|
|
||||||
import useQuillValueSync from './useQuillValueSync';
|
|
||||||
import useQuillOnChange from './useQuillOnChange';
|
|
||||||
import useMountQuill from './useMountQuill';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
function useQuill ({
|
|
||||||
disallowColors,
|
|
||||||
placeholder,
|
|
||||||
uploadImage,
|
|
||||||
onChange,
|
|
||||||
options,
|
|
||||||
value,
|
|
||||||
element,
|
|
||||||
showUploadImage
|
|
||||||
}) {
|
|
||||||
|
|
||||||
// 获取 quill 实例
|
|
||||||
const quill = useMountQuill({
|
|
||||||
element,
|
|
||||||
options,
|
|
||||||
uploadImage,
|
|
||||||
showUploadImage
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (disallowColors && quill) {
|
|
||||||
quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
|
|
||||||
delta.ops = delta.ops.map(op => {
|
|
||||||
if (op.attributes && op.attributes.color) {
|
|
||||||
const { color, ...attributes } = op.attributes;
|
|
||||||
return {
|
|
||||||
...op,
|
|
||||||
attributes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return op;
|
|
||||||
});
|
|
||||||
return delta;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
disallowColors,
|
|
||||||
quill
|
|
||||||
]);
|
|
||||||
|
|
||||||
useQuillPlaceholder(quill, placeholder);
|
|
||||||
useQuillValueSync(quill, value);
|
|
||||||
useQuillOnChange(quill, onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useQuill;
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description:
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-12 19:49:11
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-16 11:39:27
|
|
||||||
*/
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
function useQuillOnChange (quill, onChange) {
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
if (!quill) return;
|
|
||||||
if (typeof onChange !== 'function') return;
|
|
||||||
|
|
||||||
let handler;
|
|
||||||
|
|
||||||
quill.on(
|
|
||||||
'text-change',
|
|
||||||
(handler = () => {
|
|
||||||
onChange(quill.getContents()); // getContents: 检索编辑器内容
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
quill.off('text-change', handler);
|
|
||||||
}
|
|
||||||
}, [quill, onChange]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useQuillOnChange;
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description:
|
|
||||||
* @Author: tangjiang
|
|
||||||
* @Github:
|
|
||||||
* @Date: 2019-12-09 09:28:34
|
|
||||||
* @LastEditors: tangjiang
|
|
||||||
* @LastEditTime: 2019-12-16 11:39:48
|
|
||||||
*/
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
|
|
||||||
function useQuillPlaceholder (
|
|
||||||
quill,
|
|
||||||
placeholder
|
|
||||||
) {
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!quill || !quill.root) return;
|
|
||||||
quill.root.dataset.placeholder = placeholder;
|
|
||||||
}, [quill, placeholder]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useQuillPlaceholder;
|
|
@ -1,31 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
import deepEqual from './deepEqual.js'
|
|
||||||
|
|
||||||
function useQuillValueSync(quill, value) {
|
|
||||||
const [selection, setSelection] = useState(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!quill) return
|
|
||||||
|
|
||||||
const previous = quill.getContents()
|
|
||||||
const current = value
|
|
||||||
|
|
||||||
if (!deepEqual(previous, current)) {
|
|
||||||
setSelection(quill.getSelection())
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
quill.clipboard.dangerouslyPasteHTML(value, 'api')
|
|
||||||
} else {
|
|
||||||
quill.setContents(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [quill, value, setSelection])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (quill && selection) {
|
|
||||||
quill.setSelection(selection)
|
|
||||||
setSelection(null)
|
|
||||||
}
|
|
||||||
}, [quill, selection, setSelection])
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useQuillValueSync
|
|
@ -1,334 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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.less';
|
|
||||||
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)
|
|
||||||
));
|
|
Loading…
Reference in new issue