perf(前端): 邮件正文

dev/RoosterEditor
Lei OT 2 months ago
parent 888cc8214a
commit 9933fa7460

@ -4,7 +4,8 @@ import { API_HOST, EMAIL_HOST } from '@/config';
const parseHTMLString = (html) => {
const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html')
const bodyContent = doc.body.innerHTML
let bodyContent = doc.body.innerHTML
// bodyContent = bodyContent.replace(/<img/g, '<img onerror="this.onerror=null;this.src=\'https://hiana-crm.oss-accelerate.aliyuncs.com/WAMedia/afe412d4-3acf-4e79-a623-048aeb4d696a.png\';"')
return bodyContent
}
@ -57,10 +58,10 @@ const encodeEmailInfo = (info) => {
const encodeQuote = (str = '') => str.replace(/"/g, ''); //.replace(/</g,'&lt;').replace(/>/g,'&gt;')
const CSsClean = encodeQuote(info.MAI_CS).includes(',') ? encodeQuote(info.MAI_CS).split(',') : encodeQuote(info.MAI_CS).split(';');
const tosClean = (encodeQuote(info.MAI_To).includes(',') ? encodeQuote(info.MAI_To).split(',') : encodeQuote(info.MAI_To).split(';')).concat(CSsClean);
const tosClean = (encodeQuote(info.MAI_To).includes(',') ? encodeQuote(info.MAI_To).split(',') : encodeQuote(info.MAI_To).split(';')).concat(CSsClean).filter(s => s);
const replyTo = info.MAI_Direction === 1 ? info.MAI_To : info.MAI_From;
const replyToAll = (tosClean.length > 1) ?
(info.MAI_Direction === 1 ? tosClean.join(',') : `${tosClean.join(',')}, ${info.MAI_From}`)
(info.MAI_Direction === 1 ? tosClean.join(',') : [...tosClean, info.MAI_From].join(','))
: (info.MAI_Direction === 1 ? info.MAI_To : info.MAI_From)
return {
...info,
@ -77,9 +78,14 @@ const encodeEmailInfo = (info) => {
*/
export const getEmailDetailAction = async (params) => {
const { result } = await fetchJSON(`${EMAIL_HOST}/getmail`, params);
const mailType = result.MailInfo?.[0]?.MAI_ContentType || '';
let mailType = result.MailInfo?.[0]?.MAI_ContentType || '';
mailType = mailType === '' && (result.MailContent||'').includes('<html') ? 'text/html' : mailType;
return { info: encodeEmailInfo(result.MailInfo?.[0] || {}), content: mailType === 'text/html' ? parseHTMLString((result.MailContent || '').replace(/\r\n/g, '')) : (result.MailContent || ''), attachments: result?.AttachList || [] };
return {
info: { ...encodeEmailInfo(result.MailInfo?.[0] || {}), mailType },
content: mailType === 'text/html' ? parseHTMLString((result.MailContent || '').replace(/\r\n/g, '')) : result.MailContent || '',
attachments: result?.AttachList || [],
}
}
export const getEmailOrderAction = async ({ colisn }) => {

@ -0,0 +1,126 @@
import React, { useState, useEffect, useRef } from 'react'
const EmailContent = ({ id, content: MailContent, ...props }) => {
const [iframeHeight, setIframeHeight] = useState(800) // Initial height
const [content, setContent] = useState(MailContent)
const iframeRef = useRef(null)
const containerRef = useRef(null)
useEffect(() => {
setContent(MailContent)
}, [MailContent])
const setIframeContent = (iframe, content) => {
if (!iframe || !iframe.contentDocument) {
console.error('Iframe not loaded or contentDocument is null')
return
}
const doc = iframe.contentDocument
doc.open()
// doc.write(content)
doc.write(`
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
padding: 0;
/*overflow-y: hidden;*/
}
img {
max-width: 90%;
height: auto;
/*object-fit: contain;*/
}
</style>
</head>
<body>
${content}
</body>
</html>
`);
doc.close()
}
const calculateHeight = () => {
try {
if (iframeRef.current && iframeRef.current.contentDocument) {
const doc = iframeRef.current.contentDocument
const body = doc.body
if (body) {
requestAnimationFrame(() => {
const newHeight = Math.max(body.scrollHeight, body.offsetHeight, body.clientHeight)
// console.log('body.scrollHeight: ', body.scrollHeight)
// console.log('body.offsetHeight: ', body.offsetHeight)
// console.log('body.clientHeight: ', body.clientHeight)
const addMore = Math.max(Math.ceil(newHeight*0.05), 120);
// console.log('Calculated height:', newHeight, addMore)
setIframeHeight(newHeight + addMore)
})
return
} else {
console.warn('iframe body is null or undefined')
}
} else {
console.warn('iframeRef.current or contentDocument is null')
}
} catch (error) {
console.error('Error calculating height:', error)
}
setIframeHeight(200)
}
useEffect(() => {
const handleLoad = () => {
calculateHeight()
}
const currentIframe = iframeRef.current
if (currentIframe) {
currentIframe.addEventListener('load', handleLoad)
setIframeContent(currentIframe, content)
}
return () => {
if (currentIframe) {
currentIframe.removeEventListener('load', handleLoad)
}
}
}, [content])
// useEffect(() => {
// if(iframeRef.current){
// setIframeContent(iframeRef.current, content);
// calculateHeight();
// }
// }, [content])
return (
<div ref={containerRef} className='space-y-4 w-full'>
<div className='w-full relative'>
<iframe
key={id}
ref={iframeRef}
height={iframeHeight}
style={{
width: '100%',
height: `${iframeHeight}px`,
// border: '1px solid #e5e7eb',
border: 'none',
display: 'block',
}}
title='Dynamic Height Iframe'
sandbox='allow-scripts allow-same-origin'
/>
</div>
</div>
)
}
export default EmailContent

@ -10,6 +10,7 @@ import { useEmailDetail, } from '@/hooks/useEmail';
import { EMAIL_ATTA_HOST } from '@/config';
import EmailBindFormModal from './EmailBind';
import EmailDetailInline from './EmailDetailInline';
import EmailContent from './EmailContent';
/**
* @property {*} emailMsg - 邮件数据. { conversationid, actionId, order_opi, coli_sn, msgOrigin: { from, to, id, email: { subject, mai_sn, } } }
@ -141,62 +142,11 @@ const EmailDetail = ({ open, setOpen, emailMsg={}, disabled=false, ...props }) =
{mailData.info?.MAI_Subject || emailMsg?.msgOrigin?.email?.subject}
</>
}
initial={{ width: window.innerWidth - 740, height: window.innerHeight - 100, left: 300 + 24, top: 74 }}
initial={{ width: window.innerWidth - 740, height: window.innerHeight - 100, left: initialPosition.left || (300 + 24), top: initialPosition.top || 74 }}
onMove={onHandleMove}
onResize={onHandleResize}
footer={<ActionBtns className='w-full !justify-start' />}>
{/* <EmailDetailInline { ...{ mailData, loading, mailID } } /> */}
<div className='email-container flex flex-col gap-2 *:p-2 *:rounded-sm *:border-b *:border-gray-200 *:shadow-1md'>
<div className=' font-bold'>{mailData.info?.MAI_Subject || emailMsg?.msgOrigin?.subject}</div>
<div>
<div className={['flex flex-wrap justify-end', window.innerWidth < 600 ? 'flex-col' : 'flex-row '].join(' ')}>
<div className=' grow shrink basis-0 flex flex-wrap gap-2 mb-2 items-center'>
<Avatar className='' style={TagColorStyle(mailData.info?.MAI_From, true)}>
{(mailData.info?.MAI_From || '').substring(0, 1)}
</Avatar>
<div className=' flex flex-col '>
{/* <span className=' font-bold text-base'>{mailData.info?.fromName}</span> */}
<span className='text-neutral-500 text-wrap break-words break-all '>{mailData.info?.MAI_From}</span>
</div>
</div>
<div className=' shrink-0 flex flex-col justify-start gap-1 items-end'>
<ActionBtns />
<div className='text-xs '>{mailData.info?.MAI_SendDate || emailMsg.localDate}</div>
</div>
</div>
<div className='text-sm'>
<span className='text-neutral-500 pr-2 w-12 inline-block text-justify' style={{textAlignLast: 'justify'}}>收件人</span>
{mailData.info?.MAI_To}
</div>
{mailData.info?.MAI_CS && (
<div className='text-sm'>
<span className='text-neutral-500 pr-2 w-12 inline-block text-justify' style={{textAlignLast: 'justify'}}>抄送</span>
{mailData.info.MAI_CS}
</div>
)}
{mailData.info?.bcc && (
<div className='text-sm'>
<span className='text-neutral-500 pr-2 w-12 inline-block text-justify' style={{textAlignLast: 'justify'}}>密送</span>
{mailData.info.bcc}
</div>
)}
{mailData.attachments.length > 0 && (
<div className='mt-2 *:ml-2'>
<span>{mailData.attachments.length}个附件</span>
<div className='flex flex-wrap gap-2'>
{mailData.attachments.map((atta) => (
<a href={`${EMAIL_ATTA_HOST}${atta.ATI_ServerFile}`} key={atta.ATI_SN} target='_blank' rel='noreferrer'>
{atta.ATI_Name}
</a>
))}
</div>
</div>
)}
<Divider className='my-2' />
<div className={`mt-2 ${mailData.info?.MAI_ContentType === 'text/html' ? '' : 'whitespace-pre-wrap'}`} dangerouslySetInnerHTML={{ __html: mailData.content }}></div>
</div>
</div>
<EmailDetailInline { ...{ mailData, emailMsg, loading, mailID } } />
</DnDModal>
<EmailEditorPopup
open={openEmailEditor}

@ -9,6 +9,7 @@ import useStyleStore from '@/stores/StyleStore'
import { useEmailDetail, } from '@/hooks/useEmail';
import { EMAIL_ATTA_HOST } from '@/config';
import EmailBindFormModal from './EmailBind';
import EmailContent from './EmailContent';
/**
* @property {*} emailMsg - 邮件数据. { conversationid, actionId, order_opi, coli_sn, msgOrigin: { from, to, id, email: { subject, mai_sn, } } }
@ -172,7 +173,7 @@ const EmailDetailInline = ({ mailID, open, setOpen = () => {}, emailMsg={}, disa
</div>
)}
<Divider className='my-2' />
<div className='mt-2 whitespace-pre-wrap' dangerouslySetInnerHTML={{ __html: mailData.content }}></div>
{mailData.info?.mailType === 'text/html' ? <EmailContent content={mailData.content} id={mailID} key={mailID} /> : <div className='mt-2 whitespace-pre-wrap' dangerouslySetInnerHTML={{ __html: mailData.content }}></div>}
</div>
</div>
<EmailEditorPopup

@ -584,7 +584,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
</div>
)}
{showQuoteContent && (
<div contentEditable className='border-0 outline-none' onBlur={(e) => setQuoteContent(e.target.innerHTML)} dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></div>
<blockquote contentEditable className='border-0 outline-none' onBlur={(e) => setQuoteContent(`<blockquote>${e.target.innerHTML}</blockquote>`)} dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></blockquote>
)}
</DnDModal>
</ConfigProvider>

Loading…
Cancel
Save