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.
educoder/public/react/src/common/quillForEditor/index.js

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;