index.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import React, {
  2. useCallback,
  3. useEffect,
  4. useMemo,
  5. useRef,
  6. useState,
  7. } from "react";
  8. import styles from "./index.module.scss";
  9. // 引入编辑器组件
  10. // 安装---npm install braft-editor --save --force
  11. // npm install braft-utils --save --force
  12. import { ContentUtils } from "braft-utils";
  13. import BraftEditor from "braft-editor";
  14. // 引入编辑器样式
  15. import "braft-editor/dist/index.css";
  16. import classNames from "classnames";
  17. import { MessageFu } from "@/utils/message";
  18. import { fileDomInitialFu } from "@/utils/domShow";
  19. import { baseURL } from "@/utils/http";
  20. import { forwardRef, useImperativeHandle } from "react";
  21. import { API_upFile } from "@/store/action/layout";
  22. type Props = {
  23. check: boolean; //表单校验,为fasle表示不校验
  24. dirCode: string; //文件的code码
  25. isLook: boolean; //是否是查看进来
  26. ref: any; //当前自己的ref,给父组件调用
  27. myUrl: string; //上传的api地址
  28. full?: boolean;
  29. };
  30. function ZRichText({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
  31. // 添加 上传 图片的dom
  32. useEffect(() => {
  33. setTimeout(() => {
  34. const dom = document.querySelector(".bf-controlbar")!;
  35. const div = document.createElement("div");
  36. div.className = "upImgBox";
  37. // div.title = "上传图片";
  38. div.innerHTML = "上传图片/视频";
  39. div.onclick = async () => {
  40. myInput.current?.click();
  41. };
  42. dom.appendChild(div);
  43. }, 20);
  44. // 监听 富文本 的 class 变化,在全屏的时候会 富文本会添加上 fullscreen 的类
  45. // 修复顶部样式冲突问题
  46. const editorDom = document.querySelector(".bf-container") as HTMLDivElement;
  47. const observer = new MutationObserver(() => {
  48. // console.log("change");
  49. const dom = document.querySelector(".layoutRightTop") as HTMLDivElement;
  50. if (editorDom.className.includes("fullscreen")) dom.style.zIndex = "-1";
  51. else dom.style.zIndex = "100";
  52. });
  53. observer.observe(editorDom, {
  54. attributes: true,
  55. });
  56. // 销毁监听
  57. return () => {
  58. observer.disconnect();
  59. };
  60. }, []);
  61. useEffect(() => {
  62. const controlbarDom = document.querySelectorAll(".txtBox .bf-controlbar ");
  63. const contentDom = document.querySelectorAll(".txtBox .bf-content ");
  64. if (controlbarDom) {
  65. controlbarDom.forEach((v: any) => {
  66. v.style.display = isLook ? "none" : "block";
  67. });
  68. contentDom.forEach((v: any) => {
  69. v.style.height = isLook ? "100%" : "";
  70. });
  71. }
  72. }, [isLook]);
  73. // 编辑器文本
  74. const [editorValue, setEditorValue] = useState(
  75. // 初始内容
  76. BraftEditor.createEditorState("")
  77. );
  78. // 判断 富文本是否为空
  79. const isTxtFlag = useMemo(() => {
  80. const txt: string = editorValue.toHTML();
  81. if (
  82. txt.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll(" ", "") ===
  83. ""
  84. ) {
  85. return true;
  86. } else return false;
  87. }, [editorValue]);
  88. const myInput = useRef<HTMLInputElement>(null);
  89. // 上传图片
  90. const handeUpPhoto = useCallback(
  91. async (e: React.ChangeEvent<HTMLInputElement>) => {
  92. if (e.target.files) {
  93. // 拿到files信息
  94. const filesInfo = e.target.files[0];
  95. let type = ["image/jpeg", "image/png", "video/mp4"];
  96. let size = 5;
  97. let txt = "图片只支持png、jpg和jpeg格式!";
  98. let txt2 = "图片最大支持5M!";
  99. const isVideoFlag = filesInfo.name.endsWith(".mp4");
  100. // 校验格式
  101. if (!type.includes(filesInfo.type)) {
  102. e.target.value = "";
  103. if (isVideoFlag) {
  104. // 上传视频
  105. size = 500;
  106. txt = "视频只支持mp4格式!";
  107. txt2 = "视频最大支持500M!";
  108. }
  109. return MessageFu.warning(txt);
  110. }
  111. // 校验大小
  112. if (filesInfo.size > size * 1024 * 1024) {
  113. e.target.value = "";
  114. return MessageFu.warning(txt2);
  115. }
  116. // 创建FormData对象
  117. const fd = new FormData();
  118. // 把files添加进FormData对象(‘photo’为后端需要的字段)
  119. fd.append("type", isVideoFlag ? "video" : "img");
  120. fd.append("dirCode", dirCode);
  121. fd.append("file", filesInfo);
  122. e.target.value = "";
  123. try {
  124. const res = await API_upFile(fd, myUrl);
  125. if (res.code === 0) {
  126. MessageFu.success("上传成功!");
  127. // 在光标位置插入图片
  128. const newTxt = ContentUtils.insertMedias(editorValue, [
  129. {
  130. type: "IMAGE",
  131. url: baseURL + res.data.filePath,
  132. },
  133. ]);
  134. setEditorValue(newTxt);
  135. }
  136. fileDomInitialFu();
  137. } catch (error) {
  138. fileDomInitialFu();
  139. }
  140. }
  141. },
  142. [dirCode, editorValue, myUrl]
  143. );
  144. // 让父组件调用的 回显 富文本
  145. const ritxtShowFu = useCallback((val: string) => {
  146. setEditorValue(BraftEditor.createEditorState(val));
  147. }, []);
  148. // 让父组件调用的返回 富文本信息 和 表单校验 isTxtFlag为ture表示未通过校验
  149. const fatherBtnOkFu = useCallback(() => {
  150. return { val: editorValue.toHTML(), flag: isTxtFlag };
  151. }, [editorValue, isTxtFlag]);
  152. // 可以让父组件调用子组件的方法
  153. useImperativeHandle(ref, () => ({
  154. ritxtShowFu,
  155. fatherBtnOkFu,
  156. }));
  157. return (
  158. <div className={styles.ZRichText} style={{ width: full ? "100%" : "" }}>
  159. <input
  160. id="upInput"
  161. type="file"
  162. accept=".png,.jpg,.jpeg,.mp4"
  163. ref={myInput}
  164. onChange={(e) => handeUpPhoto(e)}
  165. />
  166. <div className="txtBox">
  167. <BraftEditor
  168. readOnly={isLook}
  169. placeholder="请输入内容"
  170. value={editorValue}
  171. onChange={(e) => setEditorValue(e)}
  172. imageControls={["remove"]}
  173. />
  174. </div>
  175. <div
  176. className={classNames(
  177. "noUpThumb",
  178. check && isTxtFlag ? "noUpThumbAc" : ""
  179. )}
  180. >
  181. 请输入正文!
  182. </div>
  183. </div>
  184. );
  185. }
  186. export default forwardRef(ZRichText);