|
@@ -23,6 +23,21 @@ import { baseURL } from "@/utils/http";
|
|
|
|
|
|
import { forwardRef, useImperativeHandle } from "react";
|
|
|
import { API_upFile } from "@/store/action/layout";
|
|
|
+import ZupAudio, { ZupAudioType } from "../ZupAudio";
|
|
|
+import { Button, Checkbox } from "antd";
|
|
|
+import {
|
|
|
+ ArrowDownOutlined,
|
|
|
+ DeleteOutlined,
|
|
|
+ ArrowUpOutlined,
|
|
|
+} from "@ant-design/icons";
|
|
|
+import MyPopconfirm from "../MyPopconfirm";
|
|
|
+
|
|
|
+export type SectionArrType = {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ txt: string;
|
|
|
+ fileInfo: ZupAudioType;
|
|
|
+};
|
|
|
|
|
|
type Props = {
|
|
|
check: boolean; //表单校验,为fasle表示不校验
|
|
@@ -30,59 +45,54 @@ type Props = {
|
|
|
isLook: boolean; //是否是查看进来
|
|
|
ref: any; //当前自己的ref,给父组件调用
|
|
|
myUrl: string; //上传的api地址
|
|
|
- full?: boolean;
|
|
|
};
|
|
|
|
|
|
-function ZRichTexts({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
|
|
|
- // 添加 上传 图片的dom
|
|
|
- useEffect(() => {
|
|
|
+function ZRichTexts({ check, dirCode, isLook, myUrl }: Props, ref: any) {
|
|
|
+ const [sectionArr, setSectionArr] = useState<SectionArrType[]>([
|
|
|
+ {
|
|
|
+ id: Date.now(),
|
|
|
+ name: "",
|
|
|
+ txt: "",
|
|
|
+ fileInfo: { fileName: "", filePath: "" },
|
|
|
+ },
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 当前上传 图片 视频的索引
|
|
|
+ const nowIndexRef = useRef(0);
|
|
|
+
|
|
|
+ const addDomFu = useCallback((index: number) => {
|
|
|
setTimeout(() => {
|
|
|
- const dom = document.querySelector(".bf-controlbar")!;
|
|
|
+ const domAll = document.querySelectorAll(".bf-controlbar");
|
|
|
+ const dom = domAll[index];
|
|
|
const div = document.createElement("div");
|
|
|
div.className = "upImgBox";
|
|
|
// div.title = "上传图片";
|
|
|
- div.innerHTML = "上传图片";
|
|
|
+ div.innerHTML = "上传图片/视频";
|
|
|
div.onclick = async () => {
|
|
|
+ nowIndexRef.current = index;
|
|
|
myInput.current?.click();
|
|
|
};
|
|
|
dom.appendChild(div);
|
|
|
}, 20);
|
|
|
-
|
|
|
- // 监听 富文本 的 class 变化,在全屏的时候会 富文本会添加上 fullscreen 的类
|
|
|
- // 修复顶部样式冲突问题
|
|
|
-
|
|
|
- const editorDom = document.querySelector(".bf-container") as HTMLDivElement;
|
|
|
-
|
|
|
- const observer = new MutationObserver(() => {
|
|
|
- // console.log("change");
|
|
|
- const dom = document.querySelector(".layoutRightTop") as HTMLDivElement;
|
|
|
-
|
|
|
- if (editorDom.className.includes("fullscreen")) dom.style.zIndex = "-1";
|
|
|
- else dom.style.zIndex = "100";
|
|
|
- });
|
|
|
-
|
|
|
- observer.observe(editorDom, {
|
|
|
- attributes: true,
|
|
|
- });
|
|
|
-
|
|
|
- // 销毁监听
|
|
|
- return () => {
|
|
|
- observer.disconnect();
|
|
|
- };
|
|
|
}, []);
|
|
|
|
|
|
+ // 为富文本添加 上传图片/视频 的dom
|
|
|
useEffect(() => {
|
|
|
- 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";
|
|
|
- });
|
|
|
- contentDom.forEach((v: any) => {
|
|
|
- v.style.height = isLook ? "100%" : "";
|
|
|
- });
|
|
|
- }
|
|
|
- }, [isLook]);
|
|
|
+ addDomFu(sectionArr.length - 1);
|
|
|
+ }, [addDomFu, sectionArr.length]);
|
|
|
+
|
|
|
+ // useEffect(() => {
|
|
|
+ // 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";
|
|
|
+ // });
|
|
|
+ // contentDom.forEach((v: any) => {
|
|
|
+ // v.style.height = isLook ? "100%" : "";
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ // }, [isLook]);
|
|
|
|
|
|
// 编辑器文本
|
|
|
const [editorValue, setEditorValue] = useState(
|
|
@@ -109,21 +119,35 @@ function ZRichTexts({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
|
|
|
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");
|
|
|
+
|
|
|
// 校验格式
|
|
|
- const type = ["image/jpeg", "image/png"];
|
|
|
if (!type.includes(filesInfo.type)) {
|
|
|
e.target.value = "";
|
|
|
- return MessageFu.warning("只支持png、jpg和jpeg格式!");
|
|
|
+ if (isVideoFlag) {
|
|
|
+ // 上传视频
|
|
|
+ size = 500;
|
|
|
+ txt = "视频只支持mp4格式!";
|
|
|
+ txt2 = "视频最大支持500M!";
|
|
|
+ }
|
|
|
+
|
|
|
+ return MessageFu.warning(txt);
|
|
|
}
|
|
|
// 校验大小
|
|
|
- if (filesInfo.size > 5 * 1024 * 1024) {
|
|
|
+ if (filesInfo.size > size * 1024 * 1024) {
|
|
|
e.target.value = "";
|
|
|
- return MessageFu.warning("最大支持5M!");
|
|
|
+ return MessageFu.warning(txt2);
|
|
|
}
|
|
|
// 创建FormData对象
|
|
|
const fd = new FormData();
|
|
|
// 把files添加进FormData对象(‘photo’为后端需要的字段)
|
|
|
- fd.append("type", "img");
|
|
|
+ fd.append("type", isVideoFlag ? "video" : "img");
|
|
|
fd.append("dirCode", dirCode);
|
|
|
fd.append("file", filesInfo);
|
|
|
|
|
@@ -168,24 +192,150 @@ function ZRichTexts({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
|
|
|
fatherBtnOkFu,
|
|
|
}));
|
|
|
|
|
|
+ // 是否按章节发布
|
|
|
+ const [isSection, setIsSection] = useState(false);
|
|
|
+
|
|
|
+ // 点击新增章节
|
|
|
+ const addSectionFu = useCallback(() => {
|
|
|
+ if (sectionArr.length >= 20) return MessageFu.warning("最多存在20个章节");
|
|
|
+ setSectionArr([
|
|
|
+ {
|
|
|
+ id: Date.now(),
|
|
|
+ name: "",
|
|
|
+ txt: "",
|
|
|
+ fileInfo: { fileName: "", filePath: "" },
|
|
|
+ },
|
|
|
+ ...sectionArr,
|
|
|
+ ]);
|
|
|
+ }, [sectionArr]);
|
|
|
+
|
|
|
+ // 章节音频上传成功
|
|
|
+ const upSectionFu = useCallback(
|
|
|
+ (info: ZupAudioType, index: number) => {
|
|
|
+ const arr = [...sectionArr];
|
|
|
+ arr[index].fileInfo = info;
|
|
|
+ setSectionArr(arr);
|
|
|
+ },
|
|
|
+ [sectionArr]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 章节音频删除
|
|
|
+ const delSectionFu = useCallback(
|
|
|
+ (index: number) => {
|
|
|
+ const arr = [...sectionArr];
|
|
|
+ arr[index].fileInfo = {} as ZupAudioType;
|
|
|
+ setSectionArr(arr);
|
|
|
+ },
|
|
|
+ [sectionArr]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 整个章节的删除
|
|
|
+ const delSectionAllFu = useCallback(
|
|
|
+ (id: number) => {
|
|
|
+ setSectionArr(sectionArr.filter((v) => v.id !== id));
|
|
|
+ },
|
|
|
+ [sectionArr]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 整个章节的位移
|
|
|
+ const moveSectionFu = useCallback(
|
|
|
+ (index: number, num: number) => {
|
|
|
+ const arr = [...sectionArr];
|
|
|
+ const temp = arr[index];
|
|
|
+ arr[index] = arr[index + 1];
|
|
|
+ arr[index + 1] = temp;
|
|
|
+ setSectionArr(arr);
|
|
|
+ },
|
|
|
+ [sectionArr]
|
|
|
+ );
|
|
|
+
|
|
|
return (
|
|
|
- <div className={styles.ZRichTexts} style={{ width: full ? "100%" : "" }}>
|
|
|
+ <div className={styles.ZRichTexts}>
|
|
|
<input
|
|
|
id="upInput"
|
|
|
type="file"
|
|
|
- accept=".png,.jpg,.jpeg"
|
|
|
+ accept=".png,.jpg,.jpeg,.mp4"
|
|
|
ref={myInput}
|
|
|
onChange={(e) => handeUpPhoto(e)}
|
|
|
/>
|
|
|
|
|
|
+ <div className="formRightZW">
|
|
|
+ <Checkbox
|
|
|
+ checked={isSection}
|
|
|
+ onChange={(e) => setIsSection(e.target.checked)}
|
|
|
+ >
|
|
|
+ 按章节发布
|
|
|
+ </Checkbox>
|
|
|
+
|
|
|
+ {isSection ? (
|
|
|
+ <Button type="primary" onClick={addSectionFu}>
|
|
|
+ 新增章节
|
|
|
+ </Button>
|
|
|
+ ) : (
|
|
|
+ <ZupAudio
|
|
|
+ fileInfo={sectionArr[0].fileInfo}
|
|
|
+ upDataFu={(info) => upSectionFu(info, 0)}
|
|
|
+ delFu={() => delSectionFu(0)}
|
|
|
+ dirCode={dirCode}
|
|
|
+ myUrl="cms/event/upload"
|
|
|
+ isLook={isLook}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
<div className="txtBox">
|
|
|
- <BraftEditor
|
|
|
- readOnly={isLook}
|
|
|
- placeholder="请输入内容"
|
|
|
- value={editorValue}
|
|
|
- onChange={(e) => setEditorValue(e)}
|
|
|
- imageControls={["remove"]}
|
|
|
- />
|
|
|
+ {sectionArr.map((item, index) => (
|
|
|
+ <div className="zztxtRow" key={item.id}>
|
|
|
+ {/* 顶部 */}
|
|
|
+ <div className="zztxtRow1">
|
|
|
+ <div className="zztxtRow1_1">章节1</div>
|
|
|
+ <div className="zztxtRow1_2">
|
|
|
+ <ZupAudio
|
|
|
+ fileInfo={item.fileInfo}
|
|
|
+ upDataFu={(info, index) => upSectionFu(info, index)}
|
|
|
+ delFu={(index) => delSectionFu(index)}
|
|
|
+ dirCode={dirCode}
|
|
|
+ myUrl="cms/event/upload"
|
|
|
+ isLook={isLook}
|
|
|
+ />
|
|
|
+  
|
|
|
+ <div
|
|
|
+ className={classNames(
|
|
|
+ "zztxtRow1_2Icon",
|
|
|
+ index === 0 ? "zztxtRow1_2IconNo" : ""
|
|
|
+ )}
|
|
|
+ onClick={() => moveSectionFu(index, -1)}
|
|
|
+ >
|
|
|
+ <ArrowUpOutlined title="上移" />
|
|
|
+ </div>
|
|
|
+  
|
|
|
+ <div
|
|
|
+ className={classNames(
|
|
|
+ "zztxtRow1_2Icon",
|
|
|
+ index === sectionArr.length - 1 ? "zztxtRow1_2IconNo" : ""
|
|
|
+ )}
|
|
|
+ onClick={() => moveSectionFu(index, 1)}
|
|
|
+ >
|
|
|
+ <ArrowDownOutlined title="下移" />
|
|
|
+ </div>
|
|
|
+  
|
|
|
+ <MyPopconfirm
|
|
|
+ txtK="删除"
|
|
|
+ onConfirm={() => delSectionAllFu(item.id)}
|
|
|
+ Dom={<DeleteOutlined title="删除" className="ZTbox2X" />}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {/* 主体 */}
|
|
|
+ <BraftEditor
|
|
|
+ readOnly={isLook}
|
|
|
+ placeholder="请输入内容"
|
|
|
+ value={editorValue}
|
|
|
+ onChange={(e) => setEditorValue(e)}
|
|
|
+ imageControls={["remove"]}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
</div>
|
|
|
<div
|
|
|
className={classNames(
|
|
@@ -193,7 +343,7 @@ function ZRichTexts({ check, dirCode, isLook, myUrl, full }: Props, ref: any) {
|
|
|
check && isTxtFlag ? "noUpThumbAc" : ""
|
|
|
)}
|
|
|
>
|
|
|
- 请输入正文!
|
|
|
+ 请完整输入正文!
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|