|
|
|
@ -2,12 +2,11 @@ import { useEffect, useState, useRef, useCallback, useMemo } from 'react'
|
|
|
|
|
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
|
|
|
|
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs, Radio, Typography } from 'antd'
|
|
|
|
|
import { UploadOutlined, LoadingOutlined, SaveOutlined, SendOutlined, CheckCircleOutlined } from '@ant-design/icons'
|
|
|
|
|
import '@dckj/react-better-modal/dist/index.css'
|
|
|
|
|
import useStyleStore from '@/stores/StyleStore'
|
|
|
|
|
// import useConversationStore from '@/stores/ConversationStore'
|
|
|
|
|
import useAuthStore from '@/stores/AuthStore'
|
|
|
|
|
|
|
|
|
|
import LexicalEditor from '@/components/LexicalEditor'
|
|
|
|
|
// import LexicalEditor from '@/components/LexicalEditor'
|
|
|
|
|
|
|
|
|
|
import { v4 as uuid } from 'uuid'
|
|
|
|
|
import { cloneDeep, debounce, isEmpty, olog, omitEmpty } from '@/utils/commons'
|
|
|
|
@ -24,7 +23,7 @@ import { TextIcon } from '@/components/Icons';
|
|
|
|
|
import { EMAIL_ATTA_HOST, POPUP_FEATURES } from '@/config';
|
|
|
|
|
|
|
|
|
|
import CKEditor from '@/components/CKEditor';
|
|
|
|
|
|
|
|
|
|
const {confirm} = Modal;
|
|
|
|
|
// 禁止上传的附件类型
|
|
|
|
|
// .application, .exe, .app
|
|
|
|
|
const disallowedAttachmentTypes = [
|
|
|
|
@ -48,30 +47,33 @@ const parseHTMLText = (html) => {
|
|
|
|
|
const parser = new DOMParser()
|
|
|
|
|
const dom = parser.parseFromString(html, 'text/html')
|
|
|
|
|
// Replace <br> and <p> with line breaks
|
|
|
|
|
Array.from(dom.body.querySelectorAll('br, p')).forEach((el) => {
|
|
|
|
|
el.textContent = '\n' + el.textContent
|
|
|
|
|
})
|
|
|
|
|
// Array.from(dom.body.querySelectorAll('br, p')).forEach((el) => {
|
|
|
|
|
// el.textContent = '<br>' + el.textContent
|
|
|
|
|
// })
|
|
|
|
|
// Replace <hr> with a line of dashes
|
|
|
|
|
Array.from(dom.body.querySelectorAll('hr')).forEach((el) => {
|
|
|
|
|
el.textContent = '\n------------------------------------------------------------------\n'
|
|
|
|
|
})
|
|
|
|
|
return dom.body.textContent || ''
|
|
|
|
|
// Array.from(dom.body.querySelectorAll('hr')).forEach((el) => {
|
|
|
|
|
// el.innerHTML = '<p><hr>------------------------------------------------------------------</p>'
|
|
|
|
|
// })
|
|
|
|
|
const line = '<p>------------------------------------------------------------------</p>'
|
|
|
|
|
return line+(dom.body.innerHTML || '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const generateQuoteContent = (mailData, isRichText = true) => {
|
|
|
|
|
const html = `<br><hr><p class="font-sans"><b><strong >From: </strong></b><span >${(mailData.info?.MAI_From || '')
|
|
|
|
|
// const { bodyContent } = parseHTMLString(mailData.content);
|
|
|
|
|
const html = `<br><hr><blockquote><p class="font-sans"><b><strong >From: </strong></b><span >${(mailData.info?.MAI_From || '')
|
|
|
|
|
.replace(/</g, '<')
|
|
|
|
|
.replace(/>/g, '>')} </span></p><p class="font-sans"><b><strong >Sent: </strong></b><span >${
|
|
|
|
|
mailData.info?.MAI_SendDate || ''
|
|
|
|
|
}</span></p><p class="font-sans"><b><strong >To: </strong></b><span >${(mailData.info?.MAI_To || '')
|
|
|
|
|
.replace(/</g, '<')
|
|
|
|
|
.replace(/>/g, '>')}</span></p><p class="font-sans"><b><strong >Subject: </strong></b><span >${mailData.info?.MAI_Subject || ''}</span></p><p>${
|
|
|
|
|
mailData.info?.MAI_ContentType === 'text/html' ? mailData.content : mailData.content.replace(/\r\n/g, '<br>')
|
|
|
|
|
}</p>`
|
|
|
|
|
.replace(/>/g, '>')}</span></p><p class="font-sans"><b><strong >Subject: </strong></b><span >${mailData.info?.MAI_Subject || ''}</span></p>${
|
|
|
|
|
mailData.info?.MAI_ContentType === 'text/html' ? parseHTMLString(mailData.content) : mailData.content.replace(/\r\n/g, '<br>')
|
|
|
|
|
}</blockquote>`
|
|
|
|
|
// console.log('📃', html)
|
|
|
|
|
return isRichText ? html : parseHTMLText(html)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const generateMailContent = (mailData) => `${mailData.content}<br>`
|
|
|
|
|
const generateMailContent = (mailData) => mailData.info?.MAI_ContentType === 'text/html' ? `${mailData.content}<br>` : `<p>${mailData.content.replace(/\r\n/g, '<br>')}</p>`
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 独立窗口编辑器
|
|
|
|
@ -173,7 +175,7 @@ const NewEmail = () => {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// console.log('useEffect 1---- \nform.setFieldsValue ');
|
|
|
|
|
if (isEmpty(mailData.content) && isEmpty(orderDetail.order_no)) {
|
|
|
|
|
return () => {}
|
|
|
|
|
// return () => {}
|
|
|
|
|
}
|
|
|
|
|
const docTitle = mailData.info?.MAI_Subject || 'New Email-';
|
|
|
|
|
document.title = docTitle
|
|
|
|
@ -199,9 +201,11 @@ const NewEmail = () => {
|
|
|
|
|
|
|
|
|
|
const _form2 = {
|
|
|
|
|
coli_sn: Number(pageParam.oid) || info?.MAI_COLI_SN || '',
|
|
|
|
|
mat_sn: info?.MAI_MAT_SN || defaultMAT,
|
|
|
|
|
opi_sn: info?.MAI_OPI_SN || orderDetail.opi_sn || '',
|
|
|
|
|
mat_sn: emailAccount?.mat_sn || info?.MAI_MAT_SN || defaultMAT,
|
|
|
|
|
opi_sn: emailAccount?.opi_sn || info?.MAI_OPI_SN || orderDetail.opi_sn || '',
|
|
|
|
|
}
|
|
|
|
|
const originalContentType = info?.mailType === 'text/html';
|
|
|
|
|
setIsRichText(originalContentType)
|
|
|
|
|
|
|
|
|
|
let readyToInitialContent = '';
|
|
|
|
|
let _formValues = {};
|
|
|
|
@ -216,7 +220,7 @@ const NewEmail = () => {
|
|
|
|
|
|
|
|
|
|
// 排除草稿: `编辑`有id 的邮件
|
|
|
|
|
if (!isEmpty(mailData.info) && !['edit'].includes(pageParam.action)) {
|
|
|
|
|
readyToInitialContent = orderPrefix + signatureBody
|
|
|
|
|
readyToInitialContent = orderPrefix + '<br>' + signatureBody
|
|
|
|
|
}
|
|
|
|
|
switch (pageParam.action) {
|
|
|
|
|
case 'reply':
|
|
|
|
@ -228,6 +232,7 @@ const NewEmail = () => {
|
|
|
|
|
subject: `Re: ${info.MAI_Subject || ''}`,
|
|
|
|
|
..._form2
|
|
|
|
|
}
|
|
|
|
|
readyToInitialContent += generateQuoteContent(mailData, originalContentType)
|
|
|
|
|
break
|
|
|
|
|
case 'replyall':
|
|
|
|
|
_formValues = {
|
|
|
|
@ -238,6 +243,7 @@ const NewEmail = () => {
|
|
|
|
|
subject: `Re: ${info.MAI_Subject || ''}`,
|
|
|
|
|
..._form2
|
|
|
|
|
}
|
|
|
|
|
readyToInitialContent += generateQuoteContent(mailData, originalContentType)
|
|
|
|
|
break
|
|
|
|
|
case 'forward':
|
|
|
|
|
_formValues = {
|
|
|
|
@ -246,6 +252,7 @@ const NewEmail = () => {
|
|
|
|
|
// coli_sn: pageParam.oid,
|
|
|
|
|
..._form2
|
|
|
|
|
}
|
|
|
|
|
readyToInitialContent += generateQuoteContent(mailData, originalContentType)
|
|
|
|
|
break
|
|
|
|
|
case 'edit':
|
|
|
|
|
_formValues = {
|
|
|
|
@ -257,7 +264,7 @@ const NewEmail = () => {
|
|
|
|
|
mai_sn: pageParam.quoteid,
|
|
|
|
|
..._form2
|
|
|
|
|
}
|
|
|
|
|
readyToInitialContent = '<br>'+ generateMailContent(mailData)
|
|
|
|
|
readyToInitialContent = generateMailContent(mailData)
|
|
|
|
|
setFileList(mailData.attachments.map(ele => ({ uid: ele.ATI_SN, name: ele.ATI_Name, url: ele.ATI_ServerFile, fullPath: `${EMAIL_ATTA_HOST}${ele.ATI_ServerFile}` })))
|
|
|
|
|
break
|
|
|
|
|
case 'new':
|
|
|
|
@ -268,8 +275,9 @@ const NewEmail = () => {
|
|
|
|
|
subject: `${info.MAI_Subject || templateFormValues.subject || ''}`,
|
|
|
|
|
..._form2,
|
|
|
|
|
}
|
|
|
|
|
readyToInitialContent = generateMailContent({ content: templateContent.bodycontent || readyToInitialContent || '' })
|
|
|
|
|
setFileList(mailData.attachments.map(ele => ({ uid: ele.ATI_SN, name: ele.ATI_Name, url: ele.ATI_ServerFile, fullPath: `${EMAIL_ATTA_HOST}${ele.ATI_ServerFile}` })))
|
|
|
|
|
readyToInitialContent = generateMailContent({ content: templateContent.bodycontent || readyToInitialContent || `<p></p><br>${signatureBody}` || '' })
|
|
|
|
|
// setFileList(mailData.attachments.map(ele => ({ uid: ele.ATI_SN, name: ele.ATI_Name, url: ele.ATI_ServerFile, fullPath: `${EMAIL_ATTA_HOST}${ele.ATI_ServerFile}` })))
|
|
|
|
|
setIsRichText(true)
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@ -280,7 +288,7 @@ const NewEmail = () => {
|
|
|
|
|
setInitialContent(readyToInitialContent);
|
|
|
|
|
|
|
|
|
|
return () => {}
|
|
|
|
|
}, [orderDetail.order_no, quoteLoading, loadingTamplate, emailAccountOPI, signature])
|
|
|
|
|
}, [orderDetail.order_no, quoteLoading, loadingTamplate, emailAccount, signature])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// const readFromTemplate = () => {
|
|
|
|
@ -327,7 +335,7 @@ const NewEmail = () => {
|
|
|
|
|
// const _findMat = emailListAddrMapped?.[value]
|
|
|
|
|
// setEmailMat(_findMat?.mat_sn)
|
|
|
|
|
// setEmailOPI(_findMat?.opi_sn)
|
|
|
|
|
console.log(_findMat, 'handleSwitchEmail')
|
|
|
|
|
// console.log(_findMat, 'handleSwitchEmail')
|
|
|
|
|
setEmailAccount(_findMat)
|
|
|
|
|
setEmailAccountOPI(_findMat?.opi_sn)
|
|
|
|
|
}
|
|
|
|
@ -347,12 +355,14 @@ const NewEmail = () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleEditorChange = ({ editorStateJSON, htmlContent, textContent }) => {
|
|
|
|
|
// console.log('textContent', textContent);
|
|
|
|
|
const _text = textContent.replace(/\r\n/g, '\n').replace(/\n{2,}/g, '\n')
|
|
|
|
|
// console.log('textContent---\n', textContent, 'textContent');
|
|
|
|
|
// console.log('html', html);
|
|
|
|
|
setHtmlContent(htmlContent)
|
|
|
|
|
setTextContent(textContent)
|
|
|
|
|
setTextContent(_text)
|
|
|
|
|
form.setFieldValue('content', htmlContent)
|
|
|
|
|
const { bodyText: abstract } = parseHTMLString(htmlContent, true);
|
|
|
|
|
const abstract = _text;
|
|
|
|
|
// const { bodyText: abstract } = parseHTMLString(htmlContent, true);
|
|
|
|
|
// form.setFieldValue('abstract', getAbstract(textContent))
|
|
|
|
|
const formValues = omitEmpty(form.getFieldsValue());
|
|
|
|
|
if (!isEmpty(formValues)) {
|
|
|
|
@ -502,8 +512,8 @@ const NewEmail = () => {
|
|
|
|
|
body.attaList = fileList;
|
|
|
|
|
// console.log('body', body, '\n', fileList);
|
|
|
|
|
const values = await form.validateFields()
|
|
|
|
|
const preQuoteBody = !['edit', 'new'].includes(pageParam.action) && pageParam.quoteid ? (quoteContent ? quoteContent : generateQuoteContent(mailData, isRichText)) : ''
|
|
|
|
|
body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent + preQuoteBody }) : textContent + preQuoteBody
|
|
|
|
|
// const preQuoteBody = !['edit', 'new'].includes(pageParam.action) && pageParam.quoteid ? (quoteContent ? quoteContent : generateQuoteContent(mailData, isRichText)) : ''
|
|
|
|
|
body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent }) : textContent
|
|
|
|
|
body.cc = values.cc || ''
|
|
|
|
|
body.bcc = values.bcc || ''
|
|
|
|
|
body.bcc = values.mailtype || ''
|
|
|
|
@ -522,7 +532,9 @@ const NewEmail = () => {
|
|
|
|
|
body.contenttype = isRichText ? 'text/html' : 'text/plain'
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// console.log('postSendEmail', body, '\n', msgObj);
|
|
|
|
|
// console.log('postSendEmail', body, '\n');
|
|
|
|
|
// console.log('🎈postSendEmail mailContent', body.mailcontent, '\n');
|
|
|
|
|
// throw new Error('test')
|
|
|
|
|
// return;
|
|
|
|
|
const mailSavedId = await postEmailSaveOrSend(body, isDraft)
|
|
|
|
|
form.setFieldsValue({
|
|
|
|
@ -609,7 +621,7 @@ const NewEmail = () => {
|
|
|
|
|
// labelCol={{ span: 3 }}
|
|
|
|
|
>
|
|
|
|
|
<div className='w-full flex flex-wrap gap-2 justify-start items-center text-indigo-600 pb-1 mb-2 border-x-0 border-t-0 border-b border-solid border-neutral-200'>
|
|
|
|
|
<Button type='primary' size='middle' onClick={onHandleSaveOrSend} loading={sendLoading} icon={<SendOutlined />}>
|
|
|
|
|
<Button type='primary' size='middle' onClick={() => onHandleSaveOrSend()} loading={sendLoading} icon={<SendOutlined />}>
|
|
|
|
|
发送
|
|
|
|
|
</Button>
|
|
|
|
|
<Form.Item name={'from'} rules={[{ required: true, message: '请选择发件地址' }]} >
|
|
|
|
@ -710,14 +722,14 @@ const NewEmail = () => {
|
|
|
|
|
</Form>
|
|
|
|
|
<CKEditor initData={initialContent} />
|
|
|
|
|
{/* <LexicalEditor {...{ isRichText }} onChange={handleEditorChange} defaultValue={initialContent} /> */}
|
|
|
|
|
{!isEmpty(Number(pageParam.quoteid)) && pageParam.action!=='edit' && !showQuoteContent && (
|
|
|
|
|
{/* {!isEmpty(Number(pageParam.quoteid)) && pageParam.action!=='edit' && !showQuoteContent && (
|
|
|
|
|
<div className='flex justify-start items-center ml-2'>
|
|
|
|
|
<Button className='flex gap-2 ' type='link' onClick={() => setShowQuoteContent(!showQuoteContent)}>
|
|
|
|
|
显示引用内容 ↓ {/*(不可更改)*/}
|
|
|
|
|
<Button className='flex gap-2 ' type='link' onClick={() => {
|
|
|
|
|
setShowQuoteContent(!showQuoteContent);
|
|
|
|
|
setInitialContent(pre => pre + generateQuoteContent(mailData))
|
|
|
|
|
}}>
|
|
|
|
|
显示引用内容 ↓
|
|
|
|
|
</Button>
|
|
|
|
|
{/* <Button className='flex gap-2 ' type='link' danger onClick={() => {setMergeQuote(false);setShowQuoteContent(false)}}>
|
|
|
|
|
删除引用内容
|
|
|
|
|
</Button> */}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{showQuoteContent && (
|
|
|
|
@ -726,7 +738,7 @@ const NewEmail = () => {
|
|
|
|
|
className='border-0 outline-none cursor-text'
|
|
|
|
|
onBlur={(e) => setQuoteContent(`<blockquote>${e.target.innerHTML}</blockquote>`)}
|
|
|
|
|
dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></blockquote>
|
|
|
|
|
)}
|
|
|
|
|
)} */}
|
|
|
|
|
</ConfigProvider>
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|