import React, { useCallback, 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 '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 { forwardRef, useImperativeHandle } from 'react' import { API_upFile } from '@/store/action/layout' import ZupAudio, { ZupAudioType } from '../ZupAudio' import { Button, Checkbox, Input } from 'antd' import { ArrowDownOutlined, DeleteOutlined, ArrowUpOutlined } from '@ant-design/icons' import MyPopconfirm from '../MyPopconfirm' export type SectionArrType = { id: number name: string txt: any fileInfo: ZupAudioType } type Props = { check: boolean //表单校验,为fasle表示不校验 dirCode: string //文件的code码 isLook: boolean //是否是查看进来 ref: any //当前自己的ref,给父组件调用 myUrl: string //上传的api地址 isOne?: boolean //只显示单个富文本 upAudioBtnNone?: boolean //是否能上传无障碍音频 } function ZRichTexts( { check, dirCode, isLook, myUrl, isOne = false, upAudioBtnNone = false }: Props, ref: any ) { const [sectionArr, setSectionArr] = useState([ { id: Date.now(), name: '', txt: BraftEditor.createEditorState(''), fileInfo: { fileName: '', filePath: '' } } ]) // 是否按章节发布 const [isSection, setIsSection] = useState(false) // 当前上传 图片 视频的索引 const nowIndexRef = useRef(0) // 判断 富文本是否为空 const isTxtFlag = useMemo(() => { let flag = false // 不是按章节发布,检查第一个富文本 if (!isSection) { const txt = sectionArr[0].txt.toHTML() const txtRes = txt.replaceAll(' ', '').replaceAll('

', '') if (txtRes.split('>

').length - 1 === 1 || txtRes === '') flag = true } else { // 按章节发布 检查 所有的 标题 和富文本 sectionArr.forEach((v, i) => { if (!v.name) flag = true const txt: string = v.txt.toHTML() const txtRes = txt.replaceAll(' ', '').replaceAll('

', '') if (txtRes.split('>

').length - 1 === 1 || txtRes === '') flag = true }) } return flag }, [isSection, sectionArr]) const myInput = useRef(null) // 上传图片、视频 const handeUpPhoto = useCallback( async (e: React.ChangeEvent) => { 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') || filesInfo.name.endsWith('.MP4') if (isVideoFlag) { // 上传视频 size = 500 txt = '视频只支持mp4格式!' txt2 = '视频最大支持500M!' } // 校验格式 if (!type.includes(filesInfo.type)) { e.target.value = '' return MessageFu.warning(txt) } // 校验大小 if (filesInfo.size > size * 1024 * 1024) { e.target.value = '' return MessageFu.warning(txt2) } // 创建FormData对象 const fd = new FormData() // 把files添加进FormData对象(‘photo’为后端需要的字段) fd.append('type', isVideoFlag ? 'video' : 'img') fd.append('dirCode', dirCode) fd.append('file', filesInfo) e.target.value = '' try { const res = await API_upFile(fd, myUrl) if (res.code === 0) { MessageFu.success('上传成功!') // 在光标位置插入图片 const newTxt = ContentUtils.insertMedias(sectionArr[nowIndexRef.current].txt, [ { type: isVideoFlag ? 'VIDEO' : 'IMAGE', url: baseURL + res.data.filePath } ]) const arr = [...sectionArr] arr[nowIndexRef.current].txt = newTxt setSectionArr(arr) } fileDomInitialFu() } catch (error) { fileDomInitialFu() } } }, [dirCode, myUrl, sectionArr] ) // 让父组件调用的 回显 富文本 const ritxtShowFu = useCallback((val: any) => { if (val) { setIsSection(val.isSection || false) if (val.txtArr) { const arr = val.txtArr.map((v: any) => ({ ...v, txt: BraftEditor.createEditorState(v.txt) })) setSectionArr(arr) } } }, []) // 让父组件调用的返回 富文本信息 和 表单校验 isTxtFlag为ture表示未通过校验 const fatherBtnOkFu = useCallback(() => { const arr: any[] = [] sectionArr.forEach((v, i) => { arr.push({ ...v, txt: v.txt.toHTML() }) }) const obj = { isSection: isSection, //是否按章节发布 txtArr: arr } return { val: obj, flag: isTxtFlag } }, [isSection, isTxtFlag, sectionArr]) // 可以让父组件调用子组件的方法 useImperativeHandle(ref, () => ({ ritxtShowFu, fatherBtnOkFu })) // 点击新增章节 const addSectionFu = useCallback(() => { if (sectionArr.length >= 20) return MessageFu.warning('最多存在20个章节') setSectionArr([ ...sectionArr, { id: Date.now(), name: '', txt: BraftEditor.createEditorState(''), fileInfo: { fileName: '', filePath: '' } } ]) }, [sectionArr]) // 章节音频上传成功 const upSectionFu = useCallback( (info: ZupAudioType, index: number) => { const arr = [...sectionArr] arr[index].fileInfo = info setSectionArr(arr) }, [sectionArr] ) // 章节音频删除 const delSectionFu = useCallback( (index: number) => { // console.log("ppppppppp", index); const arr = [...sectionArr] arr[index].fileInfo = { fileName: '', filePath: '' } 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 + num] arr[index + num] = temp setSectionArr(arr) }, [sectionArr] ) // 单个富文本是否输入完整 const isOneTxtFlag = useCallback( (name: string, txt: any) => { let flag = false if (!name && isSection) flag = true const txtRes: string = txt.toHTML() if (txtRes.replaceAll('

', '').replaceAll('

', '').replaceAll(' ', '') === '') flag = true return flag }, [isSection] ) return (
handeUpPhoto(e)} />
{isOne ? (
) : ( setIsSection(e.target.checked)}> 按章节发布 )} {isSection ? ( ) : (
{upAudioBtnNone ? null : ( upSectionFu(info, 0)} delFu={() => delSectionFu(0)} dirCode={dirCode} myUrl={myUrl} isLook={isLook} /> )}
)}
{sectionArr.map((item, index) => ( ))}
{`请完整输入${isSection ? '标题/' : ''}正文!`}
) } export default forwardRef(ZRichTexts)