You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.0 KiB
184 lines
5.0 KiB
/*
|
|
* @Description: quill 编辑器
|
|
* @Author: tangjiang
|
|
* @Github:
|
|
* @Date: 2019-12-18 08:49:30
|
|
* @LastEditors : tangjiang
|
|
* @LastEditTime : 2019-12-27 10:36:11
|
|
*/
|
|
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 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 { getImageUrl } from 'educoder'
|
|
import ImageBlot from './ImageBlot';
|
|
|
|
window.Quill = Quill;
|
|
window.katex = katex;
|
|
Quill.register(ImageBlot);
|
|
|
|
function QuillForEditor ({
|
|
placeholder,
|
|
readOnly,
|
|
options,
|
|
value,
|
|
imgAttrs = {}, // 指定图片的宽高
|
|
style = {},
|
|
wrapStyle = {},
|
|
showUploadImage,
|
|
onContentChange,
|
|
// getQuillContent
|
|
}) {
|
|
// toolbar 默认值
|
|
const defaultConfig = [
|
|
['bold', 'italic', 'underline'],
|
|
[{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表
|
|
[{script: 'sub'}, {script: 'super'}],
|
|
[{ 'color': [] }, { 'background': [] }],
|
|
[{header: [1,2,3,4,5,false]}],
|
|
['blockquote', 'code-block'],
|
|
['link', 'image', 'video'],
|
|
['formula'],
|
|
['clean']
|
|
];
|
|
|
|
const editorRef = useRef(null);
|
|
// quill 实例
|
|
const [quill, setQuill] = useState(null);
|
|
const [selection, setSelection] = useState(null);
|
|
|
|
// 文本内容变化时
|
|
const handleOnChange = content => {
|
|
// getQuillContent && getQuillContent(quill);
|
|
onContentChange && onContentChange(content, quill);
|
|
};
|
|
|
|
const renderOptions = options || defaultConfig;
|
|
// quill 配置信息
|
|
const quillOption = {
|
|
modules: {
|
|
toolbar: 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);
|
|
|
|
// 处理图片上传功能
|
|
_quill.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 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
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}, []);
|
|
|
|
// 设置值
|
|
useEffect(() => {
|
|
if (!quill) return
|
|
|
|
// debugger;
|
|
const previous = quill.getContents()
|
|
|
|
if (value && value.hasOwnProperty('ops')) {
|
|
// console.log(value.ops);
|
|
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)});
|
|
}
|
|
});
|
|
}
|
|
|
|
const current = value
|
|
// console.log('+++++', current);
|
|
if (!deepEqual(previous, current)) {
|
|
setSelection(quill.getSelection())
|
|
if (typeof value === 'string') {
|
|
quill.clipboard.dangerouslyPasteHTML(value, 'api')
|
|
} else {
|
|
quill.setContents(value)
|
|
}
|
|
}
|
|
}, [quill, value, setQuill]);
|
|
|
|
// 清除选择区域
|
|
useEffect(() => {
|
|
if (quill && selection) {
|
|
quill.setSelection(selection)
|
|
setSelection(null)
|
|
}
|
|
}, [quill, selection, setSelection]);
|
|
|
|
// 设置placeholder值
|
|
useEffect(() => {
|
|
if (!quill || !quill.root) return;
|
|
quill.root.dataset.placeholder = placeholder;
|
|
}, [quill, placeholder]);
|
|
|
|
// 处理内容变化
|
|
useEffect(() => {
|
|
if (!quill) return;
|
|
if (typeof handleOnChange !== 'function') return;
|
|
let handler;
|
|
quill.on(
|
|
'text-change',
|
|
(handler = () => {
|
|
handleOnChange(quill.getContents()); // getContents: 检索编辑器内容
|
|
})
|
|
);
|
|
return () => {
|
|
quill.off('text-change', handler);
|
|
}
|
|
}, [quill, handleOnChange]);
|
|
|
|
// 返回结果
|
|
return (
|
|
<div className='quill_editor_for_react_area' style={wrapStyle}>
|
|
<div ref={editorRef} style={style}></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default QuillForEditor;
|