react-draft-wysiwyg富文本组件
import React, { useState, useEffect } from 'react'
import { EditorState, convertToRaw, ContentState, Modifier } from 'draft-js'
import { Input, message } from 'antd'
// https://jpuri.github.io/react-draft-wysiwyg/#/docs
import { Editor } from 'react-draft-wysiwyg'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import draftToHtml from 'draftjs-to-html'
import htmlToDraft from 'html-to-draftjs'
import urls from '../../api/urls'
import axios from 'axios'
import { htmlFormat, addUploadToken } from '../../utils/tools'
const { TextArea } = Input
//image position is lost when converting from html to EditorState
//参考链接:https://github.com/jpuri/html-to-draftjs/issues/101
// https://github.com/jpuri/html-to-draftjs/blob/master/src/library/index.js
export default function MyEditor(props) {
const { value = {}, onChange } = props
//把html字符传转换成富文本要求的格式
const formatEditorState = (text) => {
//解决image位置无法保存的bug
const html = text ? htmlFormat(text) : ''
const contentBlock = htmlToDraft(html, (nodeName, node) => {
if (nodeName === 'img' && node instanceof HTMLImageElement) {
const entityConfig = {}
entityConfig.src = node.getAttribute
? node.getAttribute('src') || node.src
: node.src
entityConfig.alt = node.alt
entityConfig.height = node.style.height
entityConfig.width = node.style.width
if (node.style.float) {
entityConfig.alignment = node.style.float
} else {
if (node.style.textAlign) {
entityConfig.alignment = node.style.textAlign
}
}
return {
type: 'IMAGE',
mutability: 'MUTABLE',
data: entityConfig,
}
}
})
const contentState = ContentState.createFromBlockArray(
contentBlock.contentBlocks
)
const result = EditorState.createWithContent(contentState)
return result
}
const [editorState, setEditorState] = useState(formatEditorState(value.text))
const handleChange = (value) => {
setEditorState(value)
const html = draftToHtml(convertToRaw(editorState.getCurrentContent()))
//提交给后端的是html字符串
onChange({
isChange: false,
text: html,
})
}
//图片上传
const uploadImageCallBack = (file) => {
return new Promise((resolve, reject) => {
let formData = new FormData()
formData.append('file', file)
formData.append('ctype', 'course')
axios(urls.light.uploadFile, {
method: 'POST',
data: formData,
headers: {
...addUploadToken()
},
})
.then((res) => {
if (res.data.state === 1) {
resolve({ data: { link: res.data.data.fileImg } })
} else {
message.error('图片上传失败', 2)
reject(res)
}
})
.catch((err) => {
reject(err)
})
})
}
useEffect(() => {
//课程图片列表上传的图片尾部有一个加号,点击加号会自动把图片插入到富文本框光标所在的位置
if (value.isChange) {
const contentState = Modifier.replaceText(
editorState.getCurrentContent(),
editorState.getSelection(),
value.text,
);
const temp = EditorState.push(editorState, contentState, 'insert-characters')
let html = draftToHtml(convertToRaw(temp.getCurrentContent()))
html = html.replace(/</g, '<').replace(/>/g, '>')
setEditorState(formatEditorState(html))
//提交给后端的是html字符串
onChange({
isChange: false,
text: html,
})
}
}, [value])
return (
<div>
<Editor
editorState={editorState}
wrapperClassName="m-my-editor-wrapper"
editorClassName="demo-editor"
localization={{
locale: 'zh',
}}
onEditorStateChange={handleChange}
toolbar={{
image: {
urlEnabled: true,
uploadEnabled: true,
alignmentEnabled: true, // 是否显示排列按钮 相当于text-align
uploadCallback: uploadImageCallBack,
previewImage: true,
inputAccept: 'image/*',
alt: { present: false, mandatory: false, previewImage: true },
},
}}
/>
<TextArea
disabled
value={draftToHtml(convertToRaw(editorState.getCurrentContent()))}
autoSize={{ minRows: 5, maxRows: 10 }}
></TextArea>
</div>
)
}
htmlFormat:
import { html2json, json2html } from 'html2json'
//富文本html格式转换
const htmlFormat = (data) => {
const json = html2json(data)
Array.isArray(json.child) &&
json.child.forEach((item) => {
if (item.tag === 'div') {
item.tag = 'img'
if (
item.attr &&
Array.isArray(item.child) &&
item.child.length === 1 &&
item.child[0].attr &&
Array.isArray(item.child[0].attr.style)
) {
const style = item.attr.style + item.child[0].attr.style.join('')
item.attr = {
...item.child[0].attr,
}
item.attr.style = style
}
}
})
const html = json2html(json)
return html
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步