2022-07-13 10:04:01 +08:00
|
|
|
|
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
|
2022-03-10 14:24:13 +08:00
|
|
|
|
import AlertMenu from './fullScreen'
|
|
|
|
|
import E from 'wangeditor';
|
2022-07-12 16:37:11 +08:00
|
|
|
|
import { Button, message, Spin } from 'antd';
|
|
|
|
|
import { pictureDisplayPath, uploadAttachmentPath } from '@/utils/DownloadUtils';
|
2022-07-15 16:36:06 +08:00
|
|
|
|
import { isEmpty, isNotEmpty } from '@/utils/CommonUtils';
|
2022-07-13 10:04:01 +08:00
|
|
|
|
import { createNewFileBid } from '@/services/download_';
|
2022-03-10 14:24:13 +08:00
|
|
|
|
|
|
|
|
|
interface WangType {
|
|
|
|
|
braftRef: any;//挂载
|
|
|
|
|
disabled?: any;//是否可编辑
|
|
|
|
|
echo?: any;//回显内容
|
|
|
|
|
value?: any;
|
|
|
|
|
height?: number;
|
2022-07-13 10:04:01 +08:00
|
|
|
|
onChange?: (value: any) => void;
|
|
|
|
|
useImage?: boolean;//使用图片上传
|
|
|
|
|
imageId?: string;//图片objectId
|
2022-03-10 14:24:13 +08:00
|
|
|
|
}
|
|
|
|
|
let editor: any = null;
|
|
|
|
|
|
|
|
|
|
const BraftText: React.FC<WangType> = (props) => {
|
|
|
|
|
let willCreate = true;
|
|
|
|
|
|
|
|
|
|
// 菜单 key ,各个菜单不能重复-自定义工具
|
|
|
|
|
const menuKey = 'alertMenuKey'
|
|
|
|
|
//======================================================================================state
|
|
|
|
|
const [content, setContent] = useState('');
|
|
|
|
|
const [fullScreen, fullScreenSet] = useState(false);
|
2022-07-12 16:37:11 +08:00
|
|
|
|
//遮罩
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
2022-07-13 10:04:01 +08:00
|
|
|
|
//imageId
|
|
|
|
|
const objectId = useRef<string | null | undefined>(null);
|
2022-03-10 14:24:13 +08:00
|
|
|
|
const {
|
|
|
|
|
braftRef,
|
|
|
|
|
echo,
|
|
|
|
|
disabled,
|
|
|
|
|
value,
|
|
|
|
|
height,
|
|
|
|
|
onChange,
|
2022-07-13 10:04:01 +08:00
|
|
|
|
useImage,
|
|
|
|
|
imageId,
|
2022-03-10 14:24:13 +08:00
|
|
|
|
} = props;
|
|
|
|
|
|
|
|
|
|
const tools = [//工具栏
|
|
|
|
|
'head',//标题
|
|
|
|
|
'fontSize', //字号
|
2022-07-12 16:37:11 +08:00
|
|
|
|
// 'lineHeight', //行高
|
2022-03-10 14:24:13 +08:00
|
|
|
|
'foreColor', //颜色
|
|
|
|
|
'bold', //加粗
|
|
|
|
|
'italic', //斜体
|
|
|
|
|
'underline', //下划线
|
|
|
|
|
'strikeThrough', //文字删除线
|
|
|
|
|
'indent', //缩进
|
|
|
|
|
'justify', //文字对齐方式
|
2022-07-12 16:37:11 +08:00
|
|
|
|
'list', //列表
|
2022-03-10 14:24:13 +08:00
|
|
|
|
'undo', //撤销
|
2022-07-12 16:37:11 +08:00
|
|
|
|
'redo', //恢复
|
2022-03-10 14:24:13 +08:00
|
|
|
|
'fullscreen', //全屏
|
|
|
|
|
|
2022-07-13 10:04:01 +08:00
|
|
|
|
// 'image',//图片
|
2022-06-20 16:22:50 +08:00
|
|
|
|
// 'emoticon',//表情
|
|
|
|
|
// 'video',//视频
|
|
|
|
|
// 'table',//表格
|
|
|
|
|
// 'todo',//待办
|
2022-03-10 14:24:13 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
//=======================================================================================func
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (willCreate) {
|
|
|
|
|
// 注:class写法需要在componentDidMount 创建编辑器
|
2022-07-12 16:37:11 +08:00
|
|
|
|
editor = new E("#div1");
|
2022-03-10 14:24:13 +08:00
|
|
|
|
|
2022-07-13 10:04:01 +08:00
|
|
|
|
//添加图片上传
|
|
|
|
|
useImage && tools.splice(-3, 0, 'image');
|
2022-03-10 14:24:13 +08:00
|
|
|
|
//工具栏
|
|
|
|
|
editor.config.menus = tools;
|
|
|
|
|
//提示
|
2022-07-12 16:37:11 +08:00
|
|
|
|
editor.config.placeholder = '为了能顺利发布,建议您内容去掉下划线等格式,尽量以纯文本形式发布。';
|
2022-03-10 14:24:13 +08:00
|
|
|
|
// 配置 onchange 回调函数
|
|
|
|
|
editor.config.onchange = editorOnChange;
|
|
|
|
|
// 注册菜单
|
|
|
|
|
// editor.menus.extend(menuKey, AlertMenu);
|
|
|
|
|
|
|
|
|
|
// 将菜单加入到 editor.config.menus 中 const menuKey = 'alertMenuKey'
|
|
|
|
|
// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档 editor.config.menus.push(menuKey)
|
|
|
|
|
// editor.config.menus = editor.config.menus.concat(menuKey)
|
|
|
|
|
|
|
|
|
|
// 设置编辑区域高度为 500px
|
|
|
|
|
height && (editor.config.height = height);
|
2022-07-12 16:37:11 +08:00
|
|
|
|
//关闭网络上传图片
|
|
|
|
|
editor.config.showLinkImg = false;
|
|
|
|
|
// 关闭粘贴内容中的样式
|
|
|
|
|
editor.config.pasteFilterStyle = false;
|
|
|
|
|
// 忽略粘贴内容中的图片
|
|
|
|
|
editor.config.pasteIgnoreImg = true;
|
|
|
|
|
// 上传图片到服务器,对应的是controller层的@RequestMapping("/upload")
|
|
|
|
|
editor.config.uploadImgServer = uploadAttachmentPath;//接口名称
|
|
|
|
|
//自定义name,接收的时候图片文件的那么用这个,对应的是参数中的MultipartFile upimg名称,这个名称即上传到浏览器的参数名称
|
|
|
|
|
editor.config.uploadFileName = "multipartFiles";//这个需要和后台商量上传图片的名称
|
|
|
|
|
//设置请求体额外参数
|
2022-07-13 10:04:01 +08:00
|
|
|
|
if (useImage) {
|
|
|
|
|
if (isEmpty(imageId)) {
|
|
|
|
|
createNewFileBid().then(res => {//获取雪花id
|
|
|
|
|
editor.config.uploadImgParams = {
|
|
|
|
|
appCode: 'ebtp-cloud-frontend',
|
|
|
|
|
objectId: res?.id,
|
|
|
|
|
};
|
|
|
|
|
objectId.current = res?.id;
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
editor.config.uploadImgParams = {
|
|
|
|
|
appCode: 'ebtp-cloud-frontend',
|
|
|
|
|
objectId: imageId,
|
|
|
|
|
};
|
|
|
|
|
objectId.current = imageId;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-12 16:37:11 +08:00
|
|
|
|
// 将 timeout 时间改为 60s
|
|
|
|
|
editor.config.uploadImgTimeout = 60000;
|
2022-07-13 10:04:01 +08:00
|
|
|
|
// 将图片大小限制为 600k
|
|
|
|
|
editor.config.uploadImgMaxSize = 600 * 1024;
|
2022-07-12 16:37:11 +08:00
|
|
|
|
// 限制一次最多上传 1 张图片
|
|
|
|
|
editor.config.uploadImgMaxLength = 1;
|
|
|
|
|
//上传图片的错误提示默认使用alert弹出,也可以自定义用户体验更好的提示方式
|
|
|
|
|
editor.config.customAlert = function (info: string) {
|
2022-07-13 10:04:01 +08:00
|
|
|
|
const repinfo = info.replace(/0.5859375M/g, '600K');
|
2022-07-12 16:37:11 +08:00
|
|
|
|
// info 是需要提示的内容
|
|
|
|
|
message.error(repinfo);
|
|
|
|
|
};
|
|
|
|
|
// 上传图片的结果反馈
|
|
|
|
|
editor.config.uploadImgHooks = {
|
|
|
|
|
before: function (xhr: any, editor: any, files: any) {
|
|
|
|
|
// 图片上传之前触发
|
|
|
|
|
// xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件
|
|
|
|
|
// 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
|
|
|
|
|
// return {
|
|
|
|
|
// prevent: true,
|
|
|
|
|
// msg: '放弃上传'
|
|
|
|
|
// }
|
|
|
|
|
// console.log("before:",xhr)
|
2022-07-13 10:04:01 +08:00
|
|
|
|
if (isEmpty(objectId.current)) {
|
|
|
|
|
return {
|
|
|
|
|
prevent: true,
|
|
|
|
|
msg: '上传失败,原因:上传参数错误'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setLoading(true);
|
|
|
|
|
return {
|
|
|
|
|
prevent: false,
|
|
|
|
|
}
|
2022-07-12 16:37:11 +08:00
|
|
|
|
},
|
|
|
|
|
success: function (xhr: any, editor: any, result: any) {
|
|
|
|
|
// 图片上传并返回结果,图片插入成功之后触发
|
|
|
|
|
// xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
// console.log("success:",result)
|
|
|
|
|
},
|
|
|
|
|
fail: function (xhr: any, editor: any, result: any) {
|
|
|
|
|
// 图片上传并返回结果,但图片插入错误时触发
|
|
|
|
|
// xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
},
|
|
|
|
|
error: function (xhr: any, editor: any) {
|
|
|
|
|
// 图片上传出错时触发
|
|
|
|
|
// xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
|
|
|
|
|
},
|
|
|
|
|
// 上传图片超时
|
|
|
|
|
timeout: function (xhr: any) {
|
|
|
|
|
message.error('服务器超时!');
|
|
|
|
|
setLoading(false);
|
|
|
|
|
},
|
|
|
|
|
// 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
|
|
|
|
|
// (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
|
|
|
|
|
customInsert: function (insertImg: (arg0: any) => void, result: any, editor: any) {
|
|
|
|
|
// 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
|
|
|
|
|
// insertImg 是插入图片的函数,参数editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
// 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
|
|
|
|
|
if (result?.success) {
|
|
|
|
|
const url = pictureDisplayPath + '?filePath=' + result.data[0].sysStorageVO.filePath;
|
|
|
|
|
insertImg(url);
|
|
|
|
|
} else {
|
|
|
|
|
message.error('图片上传失败!');
|
|
|
|
|
}
|
|
|
|
|
setLoading(false);
|
|
|
|
|
// var url = result.result.remote_path;
|
|
|
|
|
// insertImg(url);
|
|
|
|
|
// result 必须是一个 JSON 格式字符串!!!否则报错
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-03-10 14:24:13 +08:00
|
|
|
|
/**一定要创建 */
|
|
|
|
|
editor.create();
|
|
|
|
|
|
|
|
|
|
//控制是否可编辑
|
|
|
|
|
disabled && makeDis();
|
|
|
|
|
|
|
|
|
|
willCreate = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
// 组件销毁时销毁编辑器 注:class写法需要在componentWillUnmount中调用
|
|
|
|
|
editor.destroy()
|
2022-07-15 08:58:51 +08:00
|
|
|
|
setLoading(false);
|
|
|
|
|
setContent('');
|
2022-03-10 14:24:13 +08:00
|
|
|
|
}
|
2022-07-15 16:36:06 +08:00
|
|
|
|
}, []);
|
2022-07-15 08:58:51 +08:00
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// 重新设置编辑器内容
|
2022-07-15 16:36:06 +08:00
|
|
|
|
echo && editor.txt.html(echo);
|
|
|
|
|
}, [echo])
|
2022-03-10 14:24:13 +08:00
|
|
|
|
/**
|
|
|
|
|
*提供给父级的内容
|
|
|
|
|
**/
|
|
|
|
|
useImperativeHandle(braftRef, () => ({
|
|
|
|
|
getText,
|
|
|
|
|
getHtml,
|
|
|
|
|
getHtml1,
|
|
|
|
|
makeDis,
|
2022-07-13 10:04:01 +08:00
|
|
|
|
getImageId,
|
2022-03-10 14:24:13 +08:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 获取html方法1
|
|
|
|
|
function getHtml() {
|
|
|
|
|
return content
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取html方法2
|
|
|
|
|
function getHtml1() {
|
|
|
|
|
return editor.txt.html()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取text
|
|
|
|
|
function getText() {
|
|
|
|
|
alert(editor.txt.text())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//change
|
|
|
|
|
function editorOnChange(newHtml: any) {
|
|
|
|
|
setContent(newHtml);
|
|
|
|
|
triggerChange(newHtml)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//不可编辑
|
|
|
|
|
function makeDis() {
|
|
|
|
|
editor.disable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//form取值
|
|
|
|
|
const triggerChange = (newHtml: any) => {
|
|
|
|
|
onChange?.(newHtml);
|
|
|
|
|
};
|
2022-07-13 10:04:01 +08:00
|
|
|
|
|
|
|
|
|
//获取富文本图片objectId
|
|
|
|
|
function getImageId() {
|
|
|
|
|
return objectId.current;
|
|
|
|
|
}
|
2022-03-10 14:24:13 +08:00
|
|
|
|
return (
|
2022-07-12 16:37:11 +08:00
|
|
|
|
<Spin spinning={loading} tip={"上传中……"}>
|
|
|
|
|
<div>
|
|
|
|
|
<div id="div1" style={{ textAlign: 'left' }}></div>
|
|
|
|
|
</div>
|
|
|
|
|
</Spin>
|
2022-03-10 14:24:13 +08:00
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
export default BraftText
|