import React, { useEffect, useRef, useState } from 'react' import { Prompt } from 'react-router-dom' import { DragDropContext, Droppable, Draggable, DroppableProvided, DroppableStateSnapshot, DraggableProvided, DraggableStateSnapshot, DropResult, ResponderProvided, DraggingStyle, NotDraggingStyle } from 'react-beautiful-dnd' import useRequest from '@ahooksjs/use-request/es' import { useHistory } from 'react-router-dom' import { Modal, Popover, Upload, UploadProps, message } from 'antd' import { Input } from 'antd' import { SearchOutlined } from '@ant-design/icons' import { ReactComponent as File } from 'assets/file.svg' import { ReactComponent as Image } from 'assets/image.svg' import { ReactComponent as Close } from 'assets/close.svg' import { ReactComponent as Close2 } from 'assets/close2.svg' import { FilesSvg } from './ColumnEditor' import Picture from 'Activity/components/Picture' import { ColumnType } from './components/ColumnContent' import { MaterialService } from 'Material/Material.service' import { ColumnService, IDocsList, ISaveColumnDetailParams, IUploadFileRes } from './Column.service' import { ColumnStatus } from './components/ColumnCenter' import styles from './css/ColumnEditor.module.scss' const FroalaWrapper = React.lazy(() => import('components/FroalaWrapper')) interface IColumnModifyProps { articleId: number // 文章ID(笔记或观点的ID) } let timer: NodeJS.Timeout /**修改并发布-笔记/观点 */ const ColumnModify: React.FC = props => { const history = useHistory() const froalaInstanceRef = useRef(null) const [froalaValue, setFroalaValue] = useState('') // 正文 const [selectType, setSelectType] = useState(1) // 类型1:笔记,2:观点 const [searchCategrayValue, setSearchCategrayValue] = useState('') // 搜索行业标签 const [searchCompanyValue, setSearchCompanyValue] = useState('') // 搜索公司标签 const [industryTags, setIndustryTags] = useState([]) // 选中的行业标签 const [companyTags, setCompanyTags] = useState([]) // 选中的公司标签 const [bigImg, setBigImg] = useState() // 展开的图片 const [title, setTitle] = useState('') // 标题 const [fileList, setFileList] = useState([]) // 上传文件列表 const [fileImageList, setFileImageList] = useState([]) // 上传图片列表 const [clickType, setClickType] = useState() // 记录点击操作 const [hasModify, setHasModify] = useState(false) // 是否有变动 useEffect(() => { const listener = (e: any) => { e.preventDefault() e.returnValue = '内容还未保存,确定离开吗?' } if (hasModify) { window.addEventListener('beforeunload', listener) } return () => { clearTimeout(timer) window.removeEventListener('beforeunload', listener) } }, [hasModify]) useEffect(() => { props.articleId && getColumnDetail(props.articleId) getSearchIndustry('') // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.articleId]) // 详情 const { data: columnDetail, run: getColumnDetail } = useRequest(ColumnService.getColumnDetail, { manual: true, formatResult: response => response.data.Data, onSuccess: res => { setSelectType(res.Type) setTitle(res.Title) setFroalaValue(res.Content) setFileList(res.Docs) setIndustryTags(res.IndustryTags) setCompanyTags(res.CompanyTags) const imgList = res?.ImgUrlList?.map((item: string, index: number) => ({ Id: index, ResourceUrl: item })) setFileImageList(imgList || []) } }) // 保存前的校验 const { loading: checkLoading, run: applyCheckColumnDetail } = useRequest(ColumnService.postCheckColumnDetail, { manual: true, onSuccess: res => { if (res.data.Success) { const previewDetail = formatPreviewDetail(clickType) applySaveColumnDetail(previewDetail) } else { message.error(res.data.Msg || res.data.ErrMsg) } } }) // 保存 const { loading: saveLoading, run: applySaveColumnDetail } = useRequest(ColumnService.postSaveColumnDetail, { manual: true, onSuccess: res => { res.data.Success ? message.success(res.data.Msg) : message.error(res.data.Msg || res.data.ErrMsg) res.data.Success && setHasModify(false) res.data.Success && history.push(`/column`) } }) const { data: industryList, run: getSearchIndustry } = useRequest(ColumnService.getSearchIndustry, { manual: true, formatResult: response => response.data.Data }) const { data: companyList, run: getSearchCompanyTags } = useRequest(ColumnService.getSearchCompanyTags, { manual: true, formatResult: response => response.data.Data }) const handleFormatFileList = (item: IUploadFileRes) => { return { DocName: item.ResourceName, DocSuffix: computeFileSvgShow(item?.ResourceName || '', 'suffix'), DocUrl: item.ResourceUrl // DocIcon?: string } as IDocsList } const computeFileSvgShow = (name: string, type: 'suffix' | 'svg') => { if (!name) return const suffix = name?.split('.')?.pop() if (type === 'suffix') return suffix return FilesSvg?.find(item => item.suffix === suffix)?.svg } // 上传文件 const uploadFileprops: UploadProps = { name: 'file', accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf', headers: { authorization: 'authorization-text' }, multiple: true, itemRender: () => { // 不渲染 }, beforeUpload: file => { const allowedExtensions = ['.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf'] const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase() if (!allowedExtensions.includes(fileExtension)) { message.error('只允许上传.doc, .docx, .xls, .xlsx, .ppt, .pptx 和 .pdf 文件') return Upload.LIST_IGNORE } }, customRequest: async options => { try { const response = await ColumnService.postUploadFile(options.file) if (!response.data.Success) return message.error(response.data.Msg || response.data.ErrMsg) const fileItem = handleFormatFileList(response.data?.Data) setFileList(prevList => [...(prevList || []), fileItem]) setHasModify(true) } catch (error) { console.log(error) } } } // 上传图片 const uploadImageprops: UploadProps = { name: 'file', accept: 'image/*', headers: { authorization: 'authorization-text' }, multiple: true, maxCount: 6, itemRender: () => { // 不渲染 }, beforeUpload: file => { const isPNG = file.type.toLowerCase().includes('image') if (!isPNG) { message.error(`请上传图片`) } return isPNG || Upload.LIST_IGNORE }, customRequest: async options => { try { if (fileImageList.length >= 6) return message.error('最多上传6张图片') const response = await MaterialService.postUploadImage(options.file) if (!response.data.Success) return message.error(response.data.Msg || response.data.ErrMsg) setFileImageList(prevList => { if (prevList?.length >= 6) { message.error('最多上传6张图片') return prevList } return [...(prevList || []), response.data.Data] }) setHasModify(true) } catch (error) { console.log(error) } } } // 删除文件 const handleDeleteFile = (file: IDocsList, e: React.MouseEvent) => { e.stopPropagation() setFileList(prevList => prevList.filter(item => item.DocName !== file.DocName)) setHasModify(true) } // 删除图片 const handleDeleteImg = (img: IUploadFileRes, e: React.MouseEvent) => { e.stopPropagation() setFileImageList(prevList => prevList.filter(item => item.Id !== img.Id)) setHasModify(true) } const handleSelectType = (type: number) => { setSelectType(type) setHasModify(true) } // 关闭图片查看 const handleToCloseBigImg = () => { setBigImg(undefined) } // 图片拖拽 start const reorder = (list: IUploadFileRes[], startIndex: number, endIndex: number) => { const result = Array.from(list) const [removed] = result.splice(startIndex, 1) result.splice(endIndex, 0, removed) return result } const getItemStyle = (isDragging: boolean, draggableStyle: DraggingStyle | NotDraggingStyle | undefined) => ({ userSelect: 'none', margin: `0 20px 20px 0`, background: isDragging ? '#ffffff' : '#ffffff', ...draggableStyle } as React.CSSProperties) const getListStyle = (isDraggingOver: boolean) => ({ background: isDraggingOver ? '#ffffff' : '#ffffff', display: 'flex', overflow: 'auto' }) const onDragEnd = (result: DropResult, provided: ResponderProvided) => { if (!result.destination) { return } const reorderedList = reorder(fileImageList, result.source.index, result.destination.index) setFileImageList(reorderedList) setHasModify(true) } // 图片拖拽 end // 修改并发布 const handleToClick = (type: string) => { if (!columnDetail?.IsApprovalAdmin) return message.error('您没有权限操作') if (columnDetail?.Status !== ColumnStatus.Auditing) return message.error('该文章未提交审批或已审批') if (checkLoading || saveLoading) return const typeFn: { [key: string]: () => void } = { modify: () => { Modal.confirm({ title: '提示', content: '确定修改并通过此内容的审核吗?', okText: '确定', cancelText: '取消', centered: true, closable: true, onOk: () => { handleApplyCheckColumnDetail(2) } }) } } typeFn[type]() } // 保存/发布前先校验 const handleApplyCheckColumnDetail = (doType: number) => { if (!title) return message.error('请输入标题') if (doType === 2 && !froalaValue) return message.error('请输入正文') if (doType === 2 && industryTags?.length === 0 && companyTags?.length === 0) return message.error('请选至少选择一个标签') setClickType(doType) const imgList = getImgUrlList(froalaValue) const uploadImgList = fileImageList?.map(item => item.ResourceUrl) applyCheckColumnDetail({ Content: froalaValue, ImgUrl: imgList.concat(uploadImgList) }) } // 获取正文中添加的img地址 const getImgUrlList = (str: string) => { // 匹配 标签的 src 属性 const imgRegex = /]+src="([^">]+)"/g // 使用正则表达式的 exec 方法来提取匹配的内容 let match const imgSrcList = [] while ((match = imgRegex.exec(str)) !== null) { const src = match[1] // 提取的 src 属性值 imgSrcList.push(src) } return imgSrcList } // 标题 const handleInputTitle = (e: React.ChangeEvent) => { setTitle(e.target.value) setHasModify(true) } // 正文编辑 const handleChangeText = (value: string) => { setFroalaValue(value) setHasModify(true) } // 组合预览详情 const formatPreviewDetail = (doType?: number) => { // 图片 const imgList = fileImageList?.map(item => item.ResourceUrl) return { Id: props.articleId || 0, Content: froalaValue, IndustryTags: industryTags, // 行业标签 CompanyTags: companyTags, // 公司标签 DoType: doType || 0, // 1保存2发布 ImgUrl: imgList, Docs: fileList, Title: title, Type: selectType, //类型1:笔记,2:观点 IsApprovalPersonnel: true } as ISaveColumnDetailParams } // 搜索行业标签 const handleChangeSearchCategrayValue = (e: React.ChangeEvent) => { setSearchCategrayValue(e.target.value.replace(/\s/g, '')) clearTimeout(timer) timer = setTimeout(() => { getSearchIndustry(e.target.value) }, 500) } // 搜索公司标签 const handleChangeSearchCompanyValue = (e: React.ChangeEvent) => { setSearchCompanyValue(e.target.value.replace(/\s/g, '')) clearTimeout(timer) timer = setTimeout(() => { getSearchCompanyTags(e.target.value) }, 500) } // 删除行业标签 const handleDeleteIndustry = (tag: string, e: React.MouseEvent) => { // 阻止冒泡 e.stopPropagation() setIndustryTags(prevList => prevList.filter(item => item !== tag)) setHasModify(true) } // 删除公司标签 const handleDeleteCompany = (tag: string, e: React.MouseEvent) => { e.stopPropagation() setCompanyTags(prevList => prevList.filter(item => item !== tag)) setHasModify(true) } // 添加公司标签 const handleAddCompanyTag = () => { if (!searchCompanyValue) return message.error('请输入标签') if (companyTags?.includes(searchCompanyValue)) return message.error('已存在该标签') setCompanyTags(prevList => [...prevList, searchCompanyValue]) setSearchCompanyValue('') setHasModify(true) } // 选中的标签 const handleAddTags = (actItem: string, type: 'industry' | 'company') => { if (type === 'industry') { // 如果存在则删除 if (industryTags?.includes(actItem)) { setIndustryTags(prevList => prevList.filter(item => item !== actItem)) return } setIndustryTags(prevList => [...prevList, actItem]) } else { // 如果存在则删除 if (companyTags?.includes(actItem)) { setCompanyTags(prevList => prevList.filter(item => item !== actItem)) return } setCompanyTags(prevList => [...prevList, actItem]) } setHasModify(true) } return (
发布内容
笔记 观点
{ froalaInstanceRef.current = editorInstance }} value={froalaValue} onChange={handleChangeText} configType="column" />
{fileList?.map((item, index) => (
{computeFileSvgShow(item.DocName, 'svg')} {item.DocName}
))}
{(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
{fileImageList?.map((item, index) => ( {(provided2: DraggableProvided, snapshot2: DraggableStateSnapshot) => (
图片
)}
))} {provided.placeholder}
)}
上传文件
上传图片
已选标签 {industryTags.length === 0 ? '至少添加一个标签' : `已选${industryTags.length}个标签`}
{industryTags?.map(item => (
{item}
))}
标签
{industryList?.map(item => (
{item}
))}
} trigger="click" >
{industryTags?.length ? (
{industryTags.map(item => (
{item}
))}
) : ( 添加行业标签 )}
+ 创建
已选标签 {companyTags.length === 0 ? '至少添加一个标签' : `已选${companyTags.length}个标签`}
{companyTags?.map(item => (
{item}
))}
标签
{companyList?.map(item => (
{item}
))}
} trigger="click" >
{companyTags?.length ? (
{companyTags?.map(item => (
{item}
))}
) : ( 添加公司标签 )}
修改并发布
) } export default ColumnModify