react富文本braft-editor使用
阅读文档推荐:https://www.yuque.com/braft-editor/be、https://braft.margox.cn/demos/code-highlighter、
1、安装
# 使用npm安装
npm install braft-editor --save
# 使用yarn安装
yarn add braft-editor
2、基本使用
第一步:引入braft-editor
//引入富文本编辑器
import BraftEditor from 'braft-editor'
// 引入编辑器样式
import 'braft-editor/dist/index.css'
第二步:使用富文本编辑器
import React, { useState, forwardRef } from 'react'
import BraftEditor from 'braft-editor'
// 引入编辑器样式
import 'braft-editor/dist/index.css'
function NoConMaintenance(props, ref) {
//富文本组件内容
const [editorState, setEditorState] = useState<any>(BraftEditor.createEditorState('<p></p>'))
//富文本输出的HTML
const [outputHTML, setOutputHTML] = useState<any>('<p></p>')
/**
* @Author: YuKi
* @description: 处理富文本框显示数据
* @param {Object} editorState 富文本框内容
* @return {*}
*/
const handleEditorState = (editorState) => {
setEditorState(editorState) //必须要有这一步
setOutputHTML(editorState.toHTML()) //获取富文本框里面的内容
}
return (
<>
<BraftEditor
className="my-editor"
value={editorState}
onChange={handleEditorState}
contentStyle={{
height: 210,
boxShadow: 'inset 0 1px 3px rgba(0,0,0,.1)',
border: '1px solid black',
}}
/>
<div>输出内容</div>
<div>{outputHTML}</div>
</>
)
}
export default forwardRef(NoConMaintenance)
注意事项:
1、富文本绑定的值editorState
,初始化不能为空,需要配合BraftEditor.createEditorState()
为其赋值<p></p>
。
2、富文本必须有onChange
事件,事件中的参数是当前富文本框内的值,需要手动绑定一下setEditorState(editorState)
3、自定义内置组件
富文本使用 controls
属性指定需要展示的控件,在reactHooks+Ts
项目中需要给controls
设置any类型
render () {
const controls:any = [
'undo', 'redo', 'separator',
'font-size', 'line-height', 'letter-spacing', 'separator',
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
'link', 'separator', 'hr', 'separator',
'media',
'separator',
'clear'
]
return (
<div className="editor-wrapper">
<BraftEditor
controls={controls}
/>
</div>
)
}
4、在Ant Design表单中使用
结合antd官方文档->Form->校验其他组件,采用如下两个属性
1、valuePropName
组件绑定的值,类似于Form.Item
中的name
2、getValueFromEvent
是可以把自定义组件中的onChange 的参数(如 event)转化为控件的值,必须有返回值,返回的值就是Form.Item`组件值。
使用如下:
import React, { useState,forwardRef } from 'react'
import { Form} from 'antd'
import BraftEditor from 'braft-editor'
// 引入编辑器样式
import 'braft-editor/dist/index.css'
function NoConMaintenance(props, ref) {
//表单的ref
const [aEForm] = Form.useForm()
//富文本组件内容
const [editorState, setEditorState] = useState<any>(BraftEditor.createEditorState('<p></p>'))
/**
* @Author: YuKi
* @description: 把自定义组件中的onChange 的参数(如 event)转化为控件的值
* @param {any} e
* @return {*}
*/
const normFile = (e: any) => {
return e.toHTML()
}
/**
* @Author: YuKi
* @description: 处理富文本框显示数据
* @param {Object} editorState
* @return {*}
*/
const handleEditorState = (editorState) => {
setEditorState(editorState)
}
return (
<>
<Form
name="aEfrom"
labelCol={{ span: 22 }}
wrapperCol={{ span: 22 }}
autoComplete="off"
form={aEForm}
layout="vertical"
>
<Form.Item
name="richText"
label="富文本"
valuePropName="richText"
getValueFromEvent={normFile}
rules={[{ required: true }]}
>
<BraftEditor
className="my-editor"
value={editorState}
onChange={handleEditorState}
contentStyle={{
height: 210,
boxShadow: 'inset 0 1px 3px rgba(0,0,0,.1)',
border: '1px solid black',
}}
/>
</Form.Item>
</Form>
</>
)
}
export default forwardRef(NoConMaintenance)
ps:如果需要回显数据,第一步用BraftEditor.createEditorState()
为富文本进行赋值。第二步用From表单的ref,为From组件赋值
setEditorState(BraftEditor.createEditorState(record.noticeContent))
aEForm.setFieldsValue({
richText: record.noticeContent,
})
5、集成Ant Design上传组件
以上传图片为例,braft-editor
自带媒体上传,它自己会把图片转成base64
,但是所占体积太大了。
解决方法:结合Ant Design
的上传组件,先把图片上传到服务器上,然后把图片链接放到富文本里面
第一步:利用controls
把媒体上传,去掉。
/* 富文本自定义架构 */
const controls: any = ['undo','redo','separator','font-size','line-height','letter-spacing',
'separator','text-color','bold','italic','underline', 'strike-through',
'separator','superscript','subscript','remove-styles','emoji','separator','text-indent','text-align',
'separator','headings','list-ul','list-ol','blockquote','code','separator','link','separator','hr',
'separator',// 'media','separator', 'clear',
]
第二步:采富文本extendControls
属性自定义上传组件
/**
* @Author: YuKi
* @description: 富文本上传图片前的勾子
* @param {any} option
* @return {*}
*/
const beforeUpload = (file: RcFile) => {
const isJpgOrPng =
file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/jpg'
if (!isJpgOrPng) {
message.error('You can only upload JPG、PNG、gif file!')
return Upload.LIST_IGNORE
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
message.error('Image must smaller than 2MB!')
return Upload.LIST_IGNORE
}
return isJpgOrPng && isLt2M
}
/**
* @Author: YuKi
* @description: 富文本自定义自己图片的上传实现
* @param {any} option
* @return {*}
*/
const handleUpload = (option: any) => {
const formData = new FormData()
formData.append('blobContainerName', 'NOTICE_FILE')
formData.append('fileList', option.file)
fileUpload(formData).then((rep: any) => {
setEditorState(
ContentUtils.insertMedias(editorState, [
{
type: 'IMAGE',
url:
'https://alphatest.blob.core.windows.net/' +
`${rep.data[0].blobContainerName}/${rep.data[0].fileSaveName}`,
},
]),
)
})
}
/*富文本自定义上传组件 */
const extendControls: any = [
{
key: 'antd-uploader',
type: 'component',
component: (
<Upload accept="image/*" showUploadList={false} beforeUpload={beforeUpload} customRequest={handleUpload}>
{/* 富文本里面显示的按钮 */}
<Button className="control-item button upload-button" data-title="上传图片">
<PlusOutlined />
</Button>
</Upload>
),
},
]
完整代码如下:
/*
* @Description: 菜单路由配置文件
* @Autor:
* @LastEditors: YuKi
* @Date: 2022/10/27 13:24:33
* @LastEditTime: 2023/01/06
*/
import React from 'react'
import Layout from '@/layout'
import Login from '@/pages/Login'
import { RouterType } from '@/router/router.type'
import NoPage from '@/pages/NoPage'
/* 静态页路由 */
export const noPermission: RouterType[] = [
{
path: '/login',
component: Login,
exact: true,
key: '/login',
isShow: false,
},
{
path: '/404',
component: Layout,
children: [
{
key: '404',
component: NoPage,
isShow: false,
},
],
},
]
/* 动态页路由 */
const routers: RouterType[] = [
{
path: '/',
component: Layout,
redirect: '/index/index',
exact: true,
key: '/',
isShow: false,
},
// 首页
{
path: '/index/index',
key: '/index',
isShow: true,
name: '首页',
permissions: ['index'],
component: Layout,
icon: 'HomeOutlined',
languageKey: 'menu.home',
hideChildrenInMenu: false,
breadcrumbName: '首页',
children: [
{
name: '首页',
languageKey: 'menu.home',
key: '/index/index',
path: '/index/index',
icon: 'HomeOutlined',
exact: true,
isShow: true,
breadcrumbName: '首页',
permissions: ['index:index'],
component: React.lazy(() => import('@/pages/Index')),
},
],
},
/* 个人设置 */
{
path: '/personSettings',
key: '/personSettings',
isShow: false,
name: '个人设置',
icon: 'UserOutlined',
languageKey: 'menu.personSettings',
permissions: ['personSettings'],
component: Layout,
hideChildrenInMenu: false,
breadcrumbName: '个人设置',
children: [
{
name: '个人设置',
key: '/personSettings/index',
path: '/personSettings/index',
exact: true,
icon: 'UserOutlined',
languageKey: 'menu.personSettings',
breadcrumbName: '个人设置',
isShow: false,
permissions: ['personSettings:index'],
component: React.lazy(() => import('@/pages/PersonalSettings')),
},
],
},
/* 安规维护 */
{
path: '/safetyManage',
key: '/safetyManage',
isShow: true,
name: '安规维护',
permissions: ['safetyManage'],
component: Layout,
icon: 'SaveOutlined',
languageKey: 'menu.safetyManage',
hideChildrenInMenu: false,
breadcrumbName: '安规维护',
children: [
{
name: '安规维护',
languageKey: 'menu.safetyManage',
path: '/safetyManage',
key: '/safetyManage',
icon: 'SaveOutlined',
exact: true,
isShow: true,
breadcrumbName: '安规维护',
permissions: ['safetyManage:index'],
component: React.lazy(() => import('@/pages/SafetyManage')),
},
],
},
/* App维护 */
{
path: '/AppMaintenance',
key: '/AppMaintenance',
name: 'App维护',
component: Layout,
isShow: true,
languageKey: 'menu.AppMaintenance',
breadcrumbName: 'App维护',
permissions: ['AppMaintenance'],
icon: 'AppstoreOutlined',
children: [
/* APP欢迎页面维护 */
{
path: '/AppMaintenance/AppWelcomeMaintence',
key: '/AppMaintenance/AppWelcomeMaintence',
component: React.lazy(() => import('@/pages/AppMaintenance/AppWelcomeMaintence')),
exact: true,
name: 'APP欢迎页面维护',
languageKey: 'menu.AppWelcomeMaintence',
isShow: true,
breadcrumbName: 'APP欢迎页面维护',
permissions: ['AppMaintenance:AppMaintenance:AppWelcomeMaintence'],
},
/* APP版本维护 */
{
path: '/AppMaintenance/AppVersionMaintenance',
key: '/AppMaintenance/AppVersionMaintenance',
component: React.lazy(() => import('@/pages/AppMaintenance/AppVersionMaintenance')),
exact: true,
name: 'APP版本维护',
languageKey: 'menu.AppVersionMaintenance',
isShow: true,
breadcrumbName: 'APP版本维护',
permissions: ['AppMaintenance:AppMaintenance:AppVersionMaintenance'],
},
],
},
/* 公司联系信息维护 */
{
path: '/companyContactInfoMaintenance',
key: '/companyContactInfoMaintenance',
isShow: true,
name: '公司联系信息维护',
permissions: ['companyContactInfoMaintenance'],
component: Layout,
icon: 'SnippetsOutlined',
languageKey: 'menu.companyContactInfoMaintenance',
hideChildrenInMenu: false,
breadcrumbName: '公司联系信息维护',
children: [
{
name: '公司联系信息维护',
languageKey: 'menu.companyContactInfoMaintenance',
path: '/companyContactInfoMaintenance',
key: '/companyContactInfoMaintenance',
icon: 'SnippetsOutlined',
exact: true,
isShow: true,
breadcrumbName: '公司联系信息维护',
permissions: ['companyContactInfoMaintenance:index'],
component: React.lazy(() => import('@/pages/CompanyContactInfoMaintenance')),
},
],
},
/*邮件模板管理*/
{
path: '/emailTemplate',
key: '/emailTemplate',
isShow: true,
name: ' 邮件内容模板管理',
icon: 'UsergroupDeleteOutlined',
permissions: ['emailTemplate'],
languageKey: 'menu.emailTemplate',
component: Layout,
hideChildrenInMenu: false,
breadcrumbName: ' 邮件内容模板管理',
children: [
{
name: ' 邮件内容模板管理',
icon: 'UsergroupDeleteOutlined',
key: '/emailTemplate/index',
path: '/emailTemplate/index',
exact: true,
isShow: true,
languageKey: 'menu.emailTemplate',
breadcrumbName: ' 邮件内容模板管理',
permissions: ['emailTemplate:index'],
component: React.lazy(() => import('@/pages/EmailTemplate')),
},
],
},
/*邮件内容管理*/
{
path: '/emailContentTemplate',
key: '/emailContentTemplate',
isShow: false,
name: ' 邮件内容管理',
icon: 'UsergroupDeleteOutlined',
permissions: ['emailContentTemplate'],
languageKey: 'menu.emailContentTemplate',
component: Layout,
hideChildrenInMenu: false,
breadcrumbName: ' 邮件内容管理',
children: [
{
name: ' 邮件内容模板管理',
icon: 'UsergroupDeleteOutlined',
key: '/emailContentTemplate/index',
path: '/emailContentTemplate/index',
exact: true,
isShow: false,
languageKey: 'menu.emailContentTemplate',
breadcrumbName: ' 邮件内容模板管理',
permissions: ['emailContentTemplate:index'],
component: React.lazy(() => import('@/pages/EmailTemplate/EmailContentTemplate')),
},
],
},
/* SAP管理模块 */
{
path: '/sapManage',
key: '/sapManage',
name: 'SPA管理',
component: Layout,
isShow: true,
languageKey: 'menu.SAPManage',
breadcrumbName: 'SPA管理',
permissions: ['sapManage'],
icon: 'MessageOutlined',
children: [
/* SAP接口配置管理 */
{
path: '/sapManage/sapApiManage',
key: '/sapManage/sapApiManage',
component: React.lazy(() => import('@/pages/SAPMaintenance/apiMaintenance')),
exact: true,
name: 'SAP接口配置管理',
languageKey: 'menu.sapApiManage',
isShow: true,
breadcrumbName: 'SAP接口配置管理',
permissions: ['sapManage:sapApiManage'],
},
/* SAP参数维护 */
{
path: '/sapManage/sapParamMaintain',
key: '/sapManage/sapParamMaintain',
component: React.lazy(() => import('@/pages/SAPMaintenance/paramsMaintenance')),
exact: true,
name: 'SAP参数维护',
languageKey: 'menu.sapParamMaintain',
isShow: true,
breadcrumbName: 'SAP参数维护',
permissions: ['sapManage:sapParamMaintain'],
},
/* SAP参数类别维护 */
{
path: '/sapManage/sapParamCateMain',
key: '/sapManage/sapParamCateMain',
component: React.lazy(() => import('@/pages/SAPMaintenance/paramsCateMaintenance')),
exact: true,
name: 'SAP参数类别维护',
languageKey: 'menu.sapParamCateMain',
isShow: true,
breadcrumbName: 'SAP参数类别维护',
permissions: ['sapManage:sapParamCateMain'],
},
],
},
/*语言维护*/
{
path: '/languageMaintain/index',
key: '/languageMaintain',
isShow: true,
name: '语言维护',
icon: 'UsergroupDeleteOutlined',
permissions: ['languageMaintain'],
languageKey: 'menu.emailTemplate',
component: Layout,
hideChildrenInMenu: true,
breadcrumbName: '语言维护',
children: [
{
name: '语言维护',
icon: 'UsergroupDeleteOutlined',
key: '/languageMaintain/index',
path: '/languageMaintain/index',
exact: true,
isShow: true,
languageKey: 'menu.languageMaintain',
breadcrumbName: '语言维护',
permissions: ['languageMaintain:index'],
component: React.lazy(() => import('@/pages/LanguageMaintain')),
},
],
},
/*太阳辐射显示维护*/
{
path: '/sunRadiation/index',
key: '/sunRadiation',
isShow: true,
name: '太阳辐射显示维护',
icon: 'UsergroupDeleteOutlined',
permissions: ['sunRadiation'],
languageKey: 'menu.sunRadiation',
component: Layout,
hideChildrenInMenu: true,
breadcrumbName: '太阳辐射显示维护',
children: [
{
name: '太阳辐射显示维护',
icon: 'UsergroupDeleteOutlined',
key: '/sunRadiation/index',
path: '/sunRadiation/index',
exact: true,
isShow: true,
languageKey: 'menu.sunRadiation',
breadcrumbName: '太阳辐射显示维护',
permissions: ['sunRadiation:index'],
component: React.lazy(() => import('@/pages/SunRadiation/index')),
},
],
},
/*第三方平台维护*/
{
path: '/platMaintenance/index',
key: '/platMaintenance',
isShow: true,
name: '第三方平台维护',
icon: 'UsergroupDeleteOutlined',
permissions: ['platMaintenance'],
languageKey: 'menu.platMaintenance',
component: Layout,
hideChildrenInMenu: true,
breadcrumbName: '第三方平台维护',
children: [
{
name: '第三方平台维护',
icon: 'UsergroupDeleteOutlined',
key: '/platMaintenance/index',
path: '/platMaintenance/index',
exact: true,
isShow: true,
languageKey: 'menu.platMaintenance',
breadcrumbName: '第三方平台维护',
permissions: ['platMaintenance:index'],
component: React.lazy(() => import('@/pages/ThirdPlatMaintenance')),
},
],
},
/* 站内消息维护 */
{
path: '/siteMesMaintenance',
key: '/siteMesMaintenance',
name: '站内消息维护',
component: Layout,
isShow: true,
languageKey: 'menu.siteMesMaintenance',
breadcrumbName: '站内消息维护',
permissions: ['siteMesMaintenance'],
icon: 'MessageOutlined',
children: [
/* 通知类型维护 */
{
path: '/siteMesMaintenance/templateMaintenance',
key: '/siteMesMaintenance/templateMaintenance',
component: React.lazy(() => import('@/pages/SiteMesMaintenance/TemplateMaintenance')),
exact: true,
name: '通知类型维护',
languageKey: 'menu.templateMaintenance',
isShow: true,
breadcrumbName: '通知类型维护',
permissions: ['siteMesMaintenance:templateMaintenance'],
},
/* 通知模板维护 */
{
path: '/siteMesMaintenance/typeMaintenance',
key: '/siteMesMaintenance/typeMaintenance',
component: React.lazy(() => import('@/pages/SiteMesMaintenance/TypeMaintenance')),
exact: true,
name: '通知模板维护',
languageKey: 'menu.typeMaintenance',
isShow: true,
breadcrumbName: '通知模板维护',
permissions: ['siteMesMaintenance:typeMaintenance'],
},
],
},
/*通知对象维护*/
{
path: '/noObjMaintenance',
key: '/noObjMaintenance',
isShow: false,
name: ' 通知对象维护',
permissions: ['noObjMaintenance'],
languageKey: 'menu.noObjMaintenance',
component: Layout,
hideChildrenInMenu: false,
breadcrumbName: ' 通知对象维护',
children: [
{
name: ' 通知对象维护',
key: '/noObjMaintenance/index',
path: '/noObjMaintenance/index',
exact: true,
isShow: false,
languageKey: 'menu.noObjMaintenance',
breadcrumbName: ' 通知对象维护',
permissions: ['noObjMaintenance:index'],
component: React.lazy(() => import('@/pages/SiteMesMaintenance/TypeMaintenance/components/NoObjMaintenance')),
},
],
},
/*通知内容维护*/
{
path: '/noConMaintenance',
key: '/noConMaintenance',
isShow: false,
name: ' 通知内容维护',
permissions: ['noConMaintenance'],
languageKey: 'menu.noConMaintenance',
component: Layout,
hideChildrenInMenu: false,
breadcrumbName: ' 通知内容维护',
children: [
{
name: ' 通知内容维护',
key: '/noConMaintenance/index',
path: '/noConMaintenance/index',
exact: true,
isShow: false,
languageKey: 'menu.noConMaintenance',
breadcrumbName: ' 通知内容维护',
permissions: ['noConMaintenance:index'],
component: React.lazy(() => import('@/pages/SiteMesMaintenance/TypeMaintenance/components/NoConMaintenance')),
},
],
},
/* 用户协议维护 */
{
path: '/userAgreementMaintenance',
key: '/userAgreementMaintenance',
isShow: true,
name: '用户协议维护',
permissions: ['userAgreementMaintenance'],
component: Layout,
icon: 'SnippetsOutlined',
languageKey: 'menu.userAgreementMaintenance',
hideChildrenInMenu: false,
breadcrumbName: '用户协议维护',
children: [
{
name: '用户协议维护',
languageKey: 'menu.userAgreementMaintenance',
path: '/userAgreementMaintenance/index',
key: '/userAgreementMaintenance/index',
icon: 'SnippetsOutlined',
exact: true,
isShow: true,
breadcrumbName: '用户协议维护',
permissions: ['userAgreementMaintenance:index'],
component: React.lazy(() => import('@/pages/UserAgreementMaintenance')),
},
],
},
/* 字典维护 */
{
path: '/dictionaryMaintenance',
key: '/dictionaryMaintenance',
isShow: true,
name: '字典维护',
permissions: ['dictionaryMaintenance'],
component: Layout,
icon: 'SnippetsOutlined',
languageKey: 'menu.dictionaryMaintenance',
hideChildrenInMenu: false,
breadcrumbName: '字典维护',
children: [
{
name: '字典维护',
languageKey: 'menu.dictionaryMaintenance',
path: '/dictionaryMaintenance/index',
key: '/dictionaryMaintenance/index',
icon: 'SnippetsOutlined',
exact: true,
isShow: true,
breadcrumbName: '字典维护',
permissions: ['dictionaryMaintenance:index'],
component: React.lazy(() => import('@/pages/DictionaryMaintenance')),
},
{
name: '内容维护',
languageKey: 'menu.contentRepair',
path: '/dictionaryMaintenance/contentRepair/index',
key: '/dictionaryMaintenance/contentRepair/index',
icon: 'SnippetsOutlined',
exact: true,
isShow: false,
breadcrumbName: '内容维护',
permissions: ['contentRepair:index'],
component: React.lazy(() => import('@/pages/DictionaryMaintenance/contentRepair')),
},
],
},
...noPermission,
]
export default routers