index.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import React, { useCallback, useMemo, useRef, useState } from 'react'
  2. import styles from './index.module.scss'
  3. // 引入编辑器组件
  4. // 安装---npm install braft-editor --save --force
  5. // npm install braft-utils --save --force
  6. import { ContentUtils } from 'braft-utils'
  7. import BraftEditor from 'braft-editor'
  8. // 引入编辑器样式
  9. import 'braft-editor/dist/index.css'
  10. import classNames from 'classnames'
  11. import { MessageFu } from '@/utils/message'
  12. import { fileDomInitialFu } from '@/utils/domShow'
  13. import { baseURL } from '@/utils/http'
  14. import { forwardRef, useImperativeHandle } from 'react'
  15. import { API_upFile } from '@/store/action/layout'
  16. import { Button } from 'antd'
  17. import { TopTypeType } from '@/pages/A2Abusiness/data'
  18. export type SectionArrType = {
  19. id: number
  20. txt: any
  21. }
  22. type Props = {
  23. check: boolean //表单校验,为fasle表示不校验
  24. dirCode: string //文件的code码
  25. isLook: boolean //是否是查看进来
  26. ref: any //当前自己的ref,给父组件调用
  27. myUrl: string //上传的api地址
  28. topType: TopTypeType
  29. }
  30. function ZRichTexts({ check, dirCode, isLook, myUrl, topType }: Props, ref: any) {
  31. const [sectionArr, setSectionArr] = useState<SectionArrType[]>([
  32. {
  33. id: Date.now(),
  34. txt: BraftEditor.createEditorState('')
  35. }
  36. ])
  37. // 判断 富文本是否为空
  38. const isTxtFlag = useMemo(() => {
  39. let flag = false
  40. // 不是按章节发布,检查第一个富文本
  41. const txt = sectionArr[0].txt.toHTML()
  42. const txtRes = txt.replaceAll(' ', '').replaceAll('</p><p>', '')
  43. if (txtRes === '') flag = true
  44. if (txtRes.split('></p>').length - 1 === 1 && !txtRes.includes('class="media-')) flag = true
  45. return flag
  46. }, [sectionArr])
  47. const myInput = useRef<HTMLInputElement>(null)
  48. // 上传图片、视频
  49. const handeUpPhoto = useCallback(
  50. async (e: React.ChangeEvent<HTMLInputElement>) => {
  51. if (e.target.files) {
  52. // 拿到files信息
  53. const filesInfo = e.target.files[0]
  54. let type = ['image/jpeg', 'image/png']
  55. let size = 5
  56. let txt = '图片只支持png、jpg和jpeg格式!'
  57. let txt2 = '图片最大支持5M!'
  58. // 校验格式
  59. if (!type.includes(filesInfo.type)) {
  60. e.target.value = ''
  61. return MessageFu.warning(txt)
  62. }
  63. // 校验大小
  64. if (filesInfo.size > size * 1024 * 1024) {
  65. e.target.value = ''
  66. return MessageFu.warning(txt2)
  67. }
  68. // 创建FormData对象
  69. const fd = new FormData()
  70. // 把files添加进FormData对象(‘photo’为后端需要的字段)
  71. fd.append('type', 'img')
  72. fd.append('dirCode', dirCode)
  73. fd.append('file', filesInfo)
  74. fd.append('orderType', topType)
  75. e.target.value = ''
  76. try {
  77. const res = await API_upFile(fd, myUrl)
  78. if (res.code === 0) {
  79. MessageFu.success('上传成功!')
  80. // 在光标位置插入图片
  81. const newTxt = ContentUtils.insertMedias(sectionArr[0].txt, [
  82. {
  83. type: 'IMAGE',
  84. url: baseURL + res.data.filePath
  85. }
  86. ])
  87. const arr = [...sectionArr]
  88. arr[0].txt = newTxt
  89. setSectionArr(arr)
  90. }
  91. fileDomInitialFu()
  92. } catch (error) {
  93. fileDomInitialFu()
  94. }
  95. }
  96. },
  97. [dirCode, myUrl, sectionArr, topType]
  98. )
  99. // 让父组件调用的 回显 富文本
  100. const ritxtShowFu = useCallback((val: any) => {
  101. if (val) {
  102. if (val.txtArr) {
  103. const arr = val.txtArr.map((v: any) => ({
  104. ...v,
  105. txt: BraftEditor.createEditorState(v.txt)
  106. }))
  107. setSectionArr(arr)
  108. }
  109. }
  110. }, [])
  111. // 让父组件调用的返回 富文本信息 和 表单校验 isTxtFlag为ture表示未通过校验
  112. const fatherBtnOkFu = useCallback(() => {
  113. const arr: any[] = []
  114. sectionArr.forEach((v, i) => {
  115. arr.push({
  116. ...v,
  117. txt: v.txt.toHTML()
  118. })
  119. })
  120. const obj = {
  121. isSection: 'no',
  122. txtArr: arr
  123. }
  124. return { val: obj, flag: isTxtFlag }
  125. }, [isTxtFlag, sectionArr])
  126. // 可以让父组件调用子组件的方法
  127. useImperativeHandle(ref, () => ({
  128. ritxtShowFu,
  129. fatherBtnOkFu
  130. }))
  131. // 单个富文本是否输入完整
  132. const isOneTxtFlag = useCallback((txt: any) => {
  133. let flag = false
  134. const txtRes: string = txt.toHTML()
  135. const txtRes2 = txtRes.replaceAll(' ', '').replaceAll('</p><p>', '')
  136. if (txtRes2 === '') flag = true
  137. if (txtRes2.split('></p>').length - 1 === 1 && !txtRes2.includes('class="media-')) flag = true
  138. return flag
  139. }, [])
  140. return (
  141. <div className={styles.ZRichTexts}>
  142. <input
  143. id='upInput'
  144. type='file'
  145. accept='.png,.jpg,.jpeg'
  146. ref={myInput}
  147. onChange={e => handeUpPhoto(e)}
  148. />
  149. <div className={classNames('formRightZW', isLook ? 'formRightZWLook' : '')}>
  150. <div className='formRightZWRR'>
  151. <div hidden={isLook}>
  152. <Button
  153. onClick={() => {
  154. myInput.current?.click()
  155. }}
  156. >
  157. 上传图片
  158. </Button>
  159. </div>
  160. </div>
  161. </div>
  162. <div className={classNames('txtBox', isLook ? 'txtBoxLook' : '')}>
  163. {sectionArr.map((item, index) => (
  164. <div
  165. className={classNames('zztxtRow', isOneTxtFlag(item.txt) && check ? 'zztxtRowErr' : '')}
  166. key={item.id}
  167. >
  168. {/* 主体 */}
  169. <BraftEditor
  170. readOnly={isLook}
  171. placeholder='请输入内容'
  172. value={item.txt}
  173. onChange={e => {
  174. const arr = [...sectionArr]
  175. arr[index].txt = e
  176. setSectionArr(arr)
  177. }}
  178. imageControls={['remove']}
  179. />
  180. </div>
  181. ))}
  182. </div>
  183. <div className={classNames('noUpThumb', check && isTxtFlag ? 'noUpThumbAc' : '')}>
  184. {`请输入!`}
  185. </div>
  186. </div>
  187. )
  188. }
  189. export default forwardRef(ZRichTexts)