|
@@ -1,216 +1,208 @@
|
|
|
-import React, {
|
|
|
- useCallback,
|
|
|
- useEffect,
|
|
|
- useMemo,
|
|
|
- useRef,
|
|
|
- useState,
|
|
|
-} from "react";
|
|
|
-import styles from "./index.module.scss";
|
|
|
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
|
+import styles from './index.module.scss'
|
|
|
|
|
|
// 引入编辑器组件
|
|
|
|
|
|
// 安装---npm install braft-editor --save --force
|
|
|
// npm install braft-utils --save --force
|
|
|
-import { ContentUtils } from "braft-utils";
|
|
|
-import BraftEditor from "braft-editor";
|
|
|
+import { ContentUtils } from 'braft-utils'
|
|
|
+import BraftEditor from 'braft-editor'
|
|
|
// 引入编辑器样式
|
|
|
-import "braft-editor/dist/index.css";
|
|
|
+import 'braft-editor/dist/index.css'
|
|
|
|
|
|
-import classNames from "classnames";
|
|
|
-import { MessageFu } from "@/utils/message";
|
|
|
-import { fileDomInitialFu } from "@/utils/domShow";
|
|
|
-import { baseURL } from "@/utils/http";
|
|
|
+import classNames from 'classnames'
|
|
|
+import { MessageFu } from '@/utils/message'
|
|
|
+import { fileDomInitialFu } from '@/utils/domShow'
|
|
|
+import { baseURL } from '@/utils/http'
|
|
|
|
|
|
-import { forwardRef, useImperativeHandle } from "react";
|
|
|
-import { API_upFile } from "@/store/action/layout";
|
|
|
+import { forwardRef, useImperativeHandle } from 'react'
|
|
|
+import { API_upFile } from '@/store/action/layout'
|
|
|
|
|
|
type Props = {
|
|
|
- check: boolean; //表单校验,为fasle表示不校验
|
|
|
- dirCode: string; //文件的code码
|
|
|
- isLook: boolean; //是否是查看进来
|
|
|
- ref: any; //当前自己的ref,给父组件调用
|
|
|
- myUrl: string; //上传的api地址
|
|
|
- full?: boolean;
|
|
|
-};
|
|
|
+ check: boolean //表单校验,为fasle表示不校验
|
|
|
+ dirCode: string //文件的code码
|
|
|
+ isLook: boolean //是否是查看进来
|
|
|
+ ref: any //当前自己的ref,给父组件调用
|
|
|
+ myUrl: string //上传的api地址
|
|
|
+ full?: boolean
|
|
|
+}
|
|
|
|
|
|
function ZRichText({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
|
|
|
// 添加 上传 图片的dom
|
|
|
useEffect(() => {
|
|
|
setTimeout(() => {
|
|
|
- const dom = document.querySelector(".bf-controlbar")!;
|
|
|
- const div = document.createElement("div");
|
|
|
- div.className = "upImgBox";
|
|
|
+ const dom = document.querySelector('.bf-controlbar')!
|
|
|
+ const div = document.createElement('div')
|
|
|
+ div.className = 'upImgBox'
|
|
|
// div.title = "上传图片";
|
|
|
- div.innerHTML = "上传图片/视频";
|
|
|
+ div.innerHTML = '上传图片/视频'
|
|
|
div.onclick = async () => {
|
|
|
- myInput.current?.click();
|
|
|
- };
|
|
|
- dom.appendChild(div);
|
|
|
- }, 20);
|
|
|
+ myInput.current?.click()
|
|
|
+ }
|
|
|
+ dom.appendChild(div)
|
|
|
+ }, 20)
|
|
|
|
|
|
// 监听 富文本 的 class 变化,在全屏的时候会 富文本会添加上 fullscreen 的类
|
|
|
// 修复顶部样式冲突问题
|
|
|
|
|
|
- const editorDom = document.querySelector(".bf-container") as HTMLDivElement;
|
|
|
+ const editorDom = document.querySelector('.bf-container') as HTMLDivElement
|
|
|
|
|
|
const observer = new MutationObserver(() => {
|
|
|
// console.log("change");
|
|
|
- const dom = document.querySelector(".layoutRightTop") as HTMLDivElement;
|
|
|
+ const dom = document.querySelector('.layoutRightTop') as HTMLDivElement
|
|
|
|
|
|
- if (editorDom.className.includes("fullscreen")) dom.style.zIndex = "-1";
|
|
|
- else dom.style.zIndex = "100";
|
|
|
- });
|
|
|
+ if (editorDom.className.includes('fullscreen')) dom.style.zIndex = '-1'
|
|
|
+ else dom.style.zIndex = '100'
|
|
|
+ })
|
|
|
|
|
|
observer.observe(editorDom, {
|
|
|
- attributes: true,
|
|
|
- });
|
|
|
+ attributes: true
|
|
|
+ })
|
|
|
|
|
|
// 销毁监听
|
|
|
return () => {
|
|
|
- observer.disconnect();
|
|
|
- };
|
|
|
- }, []);
|
|
|
+ observer.disconnect()
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
|
|
|
useEffect(() => {
|
|
|
- const controlbarDom = document.querySelectorAll(".txtBox .bf-controlbar ");
|
|
|
- const contentDom = document.querySelectorAll(".txtBox .bf-content ");
|
|
|
+ const controlbarDom = document.querySelectorAll('.txtBox .bf-controlbar ')
|
|
|
+ const contentDom = document.querySelectorAll('.txtBox .bf-content ')
|
|
|
if (controlbarDom) {
|
|
|
controlbarDom.forEach((v: any) => {
|
|
|
- v.style.display = isLook ? "none" : "block";
|
|
|
- });
|
|
|
+ v.style.display = isLook ? 'none' : 'block'
|
|
|
+ })
|
|
|
contentDom.forEach((v: any) => {
|
|
|
- v.style.height = isLook ? "100%" : "";
|
|
|
- });
|
|
|
+ v.style.height = isLook ? '100%' : ''
|
|
|
+ })
|
|
|
}
|
|
|
- }, [isLook]);
|
|
|
+ }, [isLook])
|
|
|
|
|
|
// 编辑器文本
|
|
|
const [editorValue, setEditorValue] = useState(
|
|
|
// 初始内容
|
|
|
- BraftEditor.createEditorState("")
|
|
|
- );
|
|
|
+ BraftEditor.createEditorState('')
|
|
|
+ )
|
|
|
|
|
|
// 判断 富文本是否为空
|
|
|
const isTxtFlag = useMemo(() => {
|
|
|
- const txt: string = editorValue.toHTML();
|
|
|
- if (
|
|
|
- txt.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll(" ", "") ===
|
|
|
- ""
|
|
|
- ) {
|
|
|
- return true;
|
|
|
- } else return false;
|
|
|
- }, [editorValue]);
|
|
|
+ const txt: string = editorValue.toHTML()
|
|
|
+ if (txt.replaceAll('<p>', '').replaceAll('</p>', '').replaceAll(' ', '') === '') {
|
|
|
+ return true
|
|
|
+ } else return false
|
|
|
+ }, [editorValue])
|
|
|
|
|
|
- const myInput = useRef<HTMLInputElement>(null);
|
|
|
+ const myInput = useRef<HTMLInputElement>(null)
|
|
|
|
|
|
// 上传图片
|
|
|
const handeUpPhoto = useCallback(
|
|
|
async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
if (e.target.files) {
|
|
|
// 拿到files信息
|
|
|
- const filesInfo = e.target.files[0];
|
|
|
-
|
|
|
- let type = ["image/jpeg", "image/png", "video/mp4"];
|
|
|
- let size = 5;
|
|
|
- let txt = "图片只支持png、jpg和jpeg格式!";
|
|
|
- let txt2 = "图片最大支持5M!";
|
|
|
-
|
|
|
- const isVideoFlag = filesInfo.name.endsWith(".mp4");
|
|
|
-
|
|
|
- // 校验格式
|
|
|
- if (!type.includes(filesInfo.type)) {
|
|
|
- e.target.value = "";
|
|
|
- if (isVideoFlag) {
|
|
|
- // 上传视频
|
|
|
- size = 500;
|
|
|
- txt = "视频只支持mp4格式!";
|
|
|
- txt2 = "视频最大支持500M!";
|
|
|
- }
|
|
|
-
|
|
|
- return MessageFu.warning(txt);
|
|
|
+ const filesInfo = e.target.files[0]
|
|
|
+
|
|
|
+ const isVideo = filesInfo.name.endsWith('.mp4')
|
|
|
+
|
|
|
+ const mediaConfig = isVideo
|
|
|
+ ? {
|
|
|
+ types: ['video/mp4'],
|
|
|
+ maxSize: 500,
|
|
|
+ errorText: '视频只支持mp4格式!',
|
|
|
+ errorSizeText: '视频最大支持500M!',
|
|
|
+ mediaType: 'VIDEO'
|
|
|
+ }
|
|
|
+ : {
|
|
|
+ types: ['image/jpeg', 'image/png'],
|
|
|
+ maxSize: 5,
|
|
|
+ errorText: '图片只支持png、jpg和jpeg格式!',
|
|
|
+ errorSizeText: '图片最大支持5M!',
|
|
|
+ mediaType: 'IMAGE'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式校验
|
|
|
+ if (!mediaConfig.types.includes(filesInfo.type)) {
|
|
|
+ e.target.value = ''
|
|
|
+ return MessageFu.warning(mediaConfig.errorText)
|
|
|
}
|
|
|
- // 校验大小
|
|
|
- if (filesInfo.size > size * 1024 * 1024) {
|
|
|
- e.target.value = "";
|
|
|
- return MessageFu.warning(txt2);
|
|
|
+
|
|
|
+ // 大小校验
|
|
|
+ if (filesInfo.size > mediaConfig.maxSize) {
|
|
|
+ e.target.value = ''
|
|
|
+ return MessageFu.warning(mediaConfig.errorSizeText)
|
|
|
}
|
|
|
+
|
|
|
// 创建FormData对象
|
|
|
- const fd = new FormData();
|
|
|
+ const fd = new FormData()
|
|
|
// 把files添加进FormData对象(‘photo’为后端需要的字段)
|
|
|
- fd.append("type", isVideoFlag ? "video" : "img");
|
|
|
- fd.append("dirCode", dirCode);
|
|
|
- fd.append("file", filesInfo);
|
|
|
+ fd.append('type', isVideo ? 'video' : 'img')
|
|
|
+ fd.append('dirCode', dirCode)
|
|
|
+ fd.append('file', filesInfo)
|
|
|
|
|
|
- e.target.value = "";
|
|
|
+ e.target.value = ''
|
|
|
|
|
|
try {
|
|
|
- const res = await API_upFile(fd, myUrl);
|
|
|
+ const res = await API_upFile(fd, myUrl)
|
|
|
if (res.code === 0) {
|
|
|
- MessageFu.success("上传成功!");
|
|
|
+ MessageFu.success('上传成功!')
|
|
|
// 在光标位置插入图片
|
|
|
const newTxt = ContentUtils.insertMedias(editorValue, [
|
|
|
{
|
|
|
- type: "IMAGE",
|
|
|
- url: baseURL + res.data.filePath,
|
|
|
- },
|
|
|
- ]);
|
|
|
+ type: mediaConfig.mediaType,
|
|
|
+ url: baseURL + res.data.filePath
|
|
|
+ }
|
|
|
+ ])
|
|
|
|
|
|
- setEditorValue(newTxt);
|
|
|
+ setEditorValue(newTxt)
|
|
|
}
|
|
|
- fileDomInitialFu();
|
|
|
+ fileDomInitialFu()
|
|
|
} catch (error) {
|
|
|
- fileDomInitialFu();
|
|
|
+ fileDomInitialFu()
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
[dirCode, editorValue, myUrl]
|
|
|
- );
|
|
|
+ )
|
|
|
|
|
|
// 让父组件调用的 回显 富文本
|
|
|
const ritxtShowFu = useCallback((val: string) => {
|
|
|
- setEditorValue(BraftEditor.createEditorState(val));
|
|
|
- }, []);
|
|
|
+ setEditorValue(BraftEditor.createEditorState(val))
|
|
|
+ }, [])
|
|
|
|
|
|
// 让父组件调用的返回 富文本信息 和 表单校验 isTxtFlag为ture表示未通过校验
|
|
|
const fatherBtnOkFu = useCallback(() => {
|
|
|
- return { val: editorValue.toHTML(), flag: isTxtFlag };
|
|
|
- }, [editorValue, isTxtFlag]);
|
|
|
+ return { val: editorValue.toHTML(), flag: isTxtFlag }
|
|
|
+ }, [editorValue, isTxtFlag])
|
|
|
|
|
|
// 可以让父组件调用子组件的方法
|
|
|
useImperativeHandle(ref, () => ({
|
|
|
ritxtShowFu,
|
|
|
- fatherBtnOkFu,
|
|
|
- }));
|
|
|
+ fatherBtnOkFu
|
|
|
+ }))
|
|
|
|
|
|
return (
|
|
|
- <div className={styles.ZRichText} style={{ width: full ? "100%" : "" }}>
|
|
|
+ <div className={styles.ZRichText} style={{ width: full ? '100%' : '' }}>
|
|
|
<input
|
|
|
- id="upInput"
|
|
|
- type="file"
|
|
|
- accept=".png,.jpg,.jpeg,.mp4"
|
|
|
+ id='upInput'
|
|
|
+ type='file'
|
|
|
+ accept='.png,.jpg,.jpeg,.mp4'
|
|
|
ref={myInput}
|
|
|
- onChange={(e) => handeUpPhoto(e)}
|
|
|
+ onChange={e => handeUpPhoto(e)}
|
|
|
/>
|
|
|
|
|
|
- <div className="txtBox">
|
|
|
+ <div className='txtBox'>
|
|
|
<BraftEditor
|
|
|
readOnly={isLook}
|
|
|
- placeholder="请输入内容"
|
|
|
+ placeholder='请输入内容'
|
|
|
value={editorValue}
|
|
|
- onChange={(e) => setEditorValue(e)}
|
|
|
- imageControls={["remove"]}
|
|
|
+ onChange={e => setEditorValue(e)}
|
|
|
+ imageControls={['remove']}
|
|
|
/>
|
|
|
</div>
|
|
|
- <div
|
|
|
- className={classNames(
|
|
|
- "noUpThumb",
|
|
|
- check && isTxtFlag ? "noUpThumbAc" : ""
|
|
|
- )}
|
|
|
- >
|
|
|
+ <div className={classNames('noUpThumb', check && isTxtFlag ? 'noUpThumbAc' : '')}>
|
|
|
请输入正文!
|
|
|
</div>
|
|
|
</div>
|
|
|
- );
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
-export default forwardRef(ZRichText);
|
|
|
+export default forwardRef(ZRichText)
|