test: iframe 渲染邮件正文

dev/full-email
Lei OT 2 months ago
parent 00384ca10c
commit 1ff690f606

@ -4,7 +4,8 @@ import { API_HOST, EMAIL_HOST } from '@/config';
const parseHTMLString = (html) => { const parseHTMLString = (html) => {
const parser = new DOMParser() const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html') 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 return bodyContent
} }
@ -77,9 +78,14 @@ const encodeEmailInfo = (info) => {
*/ */
export const getEmailDetailAction = async (params) => { export const getEmailDetailAction = async (params) => {
const { result } = await fetchJSON(`${EMAIL_HOST}/getmail`, 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 }) => { export const getEmailOrderAction = async ({ colisn }) => {

@ -51,6 +51,7 @@ export const useEmailDetail = (mai_sn, data) => {
try { try {
setLoading(true) setLoading(true)
const data = await getEmailDetailAction({ mai_sn }) const data = await getEmailDetailAction({ mai_sn })
console.log('mail detail', data);
setMailData(data) setMailData(data)
setColiSN(data.info.MAI_COLI_SN) setColiSN(data.info.MAI_COLI_SN)
setLoading(false) setLoading(false)

@ -0,0 +1,126 @@
import React, { useState, useEffect, useRef } from 'react'
const EmailContent = ({ id, content: MailContent, ...props }) => {
const [iframeHeight, setIframeHeight] = useState(200) // 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)
console.log('Calculated height:', newHeight)
setIframeHeight(newHeight + 120)
})
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 = () => {
console.log('iframe loaded')
calculateHeight()
}
const currentIframe = iframeRef.current
if (currentIframe) {
currentIframe.addEventListener('load', handleLoad)
setIframeContent(currentIframe, content) // Set content on initial load
}
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

@ -0,0 +1,156 @@
import React, { useState, useEffect, useRef } from 'react';
const EmailContent = ({ content: MailContent, ...props}) => {
const [iframeHeight, setIframeHeight] = useState(200); // Initial height
const [content, setContent] = useState(MailContent);
const iframeRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
setContent(MailContent);
}, [MailContent]);
const calculateHeight = () => {
if (iframeRef.current && iframeRef.current.contentDocument) {
const doc = iframeRef.current.contentDocument;
const body = doc.body;
// Ensure the body is loaded before attempting to get its height.
if (body) {
// Use Math.max to get the larger of scrollHeight, clientHeight, and offsetHeight
const newHeight = Math.max(
body.scrollHeight,
body.offsetHeight,
body.clientHeight
);
setIframeHeight(newHeight);
}
}
};
useEffect(() => {
const handleLoad = () => {
calculateHeight();
};
if (iframeRef.current) {
iframeRef.current.addEventListener('load', handleLoad);
}
return () => {
if (iframeRef.current) {
iframeRef.current.removeEventListener('load', handleLoad);
}
};
}, [content]); // Recalculate when content changes
// const handleContentChange = (newContent) => {
// setContent(newContent);
// };
// const handleReset = () => {
// const defaultContent = `
// <h1>Welcome to the Iframe Test</h1>
// <p>This is the default content inside the iframe. It should adjust its height dynamically.</p>
// <h2>Section 1</h2>
// <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
// `;
// setContent(defaultContent);
// };
return (
<div ref={containerRef} className="space-y-4 w-full">
{/* <h1 className="text-2xl font-bold text-gray-200">Iframe Height Calculator</h1>
<p className="text-gray-400">
This example demonstrates how to dynamically adjust the height of an iframe to fit its content, eliminating vertical scrollbars.
</p> */}
<div className="w-full relative">
<iframe
ref={iframeRef}
style={{
width: '100%',
height: `${iframeHeight}px`,
border: '1px solid #e5e7eb',
display: 'block', // Ensure iframe is treated as a block element
}}
title="Dynamic Height Iframe"
sandbox="allow-scripts allow-same-origin" // Important for script execution and accessing content
/>
{/* Render the content directly. The iframe will load this. */}
{iframeRef.current?.contentDocument && (
<div className="p-4">
{/* Added styling for better rendering inside the iframe */}
<style>{`
body {
margin: 0;
padding: 0;
font-family: sans-serif; /* Added a default font */
line-height: 1.5; /* Improved default line height */
}
p {
margin-bottom: 1em;
}
h1 {
font-size: 2em;
margin-bottom: 0.75em;
}
h2 {
font-size: 1.5em;
margin-bottom: 0.6em;
}
h3 {
font-size: 1.17em;
margin-bottom: 0.5em;
}
a {
color: #0078d7; /* Standard link color */
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
`}</style>
<div dangerouslySetInnerHTML={{ __html: content }} />
</div>
)}
</div>
{/* <div className="flex flex-wrap gap-4">
<button
onClick={() =>
handleContentChange(`
<h1>Dynamic Content Update</h1>
<p>This content was added dynamically!</p>
<ul><li>Item 1</li><li>Item 2</li></ul>
<p>The iframe should resize to fit this new content.</p>
<p>Testing a very long paragraph to see how the iframe handles wrapping and height adjustment. This is a long sentence that should wrap to multiple lines within the iframe's body, and the iframe should increase its height to accommodate all of this text without creating a vertical scrollbar. We want the iframe to behave like a native div element in terms of height.</p>
`)
}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Change Content
</button>
<button
onClick={() =>
handleContentChange(`
<h1>Another Content Change</h1>
<p>This is different content.</p>
<ol><li>Step 1</li><li>Step 2</li></ol>
`)
}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
>
Change Content Again
</button>
<button
onClick={handleReset}
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
>
Reset Content
</button>
</div> */}
</div>
);
};
export default EmailContent;

@ -10,6 +10,7 @@ import { useEmailDetail, } from '@/hooks/useEmail';
import { EMAIL_ATTA_HOST } from '@/config'; import { EMAIL_ATTA_HOST } from '@/config';
import EmailBindFormModal from './EmailBind'; import EmailBindFormModal from './EmailBind';
import EmailDetailInline from './EmailDetailInline'; import EmailDetailInline from './EmailDetailInline';
import EmailContent from './EmailContent';
/** /**
* @property {*} emailMsg - 邮件数据. { conversationid, actionId, order_opi, coli_sn, msgOrigin: { from, to, id, email: { subject, mai_sn, } } } * @property {*} emailMsg - 邮件数据. { conversationid, actionId, order_opi, coli_sn, msgOrigin: { from, to, id, email: { subject, mai_sn, } } }
@ -141,7 +142,7 @@ const EmailDetail = ({ open, setOpen, emailMsg={}, disabled=false, ...props }) =
{mailData.info?.MAI_Subject || emailMsg?.msgOrigin?.email?.subject} {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} onMove={onHandleMove}
onResize={onHandleResize} onResize={onHandleResize}
footer={<ActionBtns className='w-full !justify-start' />}> footer={<ActionBtns className='w-full !justify-start' />}>
@ -194,7 +195,9 @@ const EmailDetail = ({ open, setOpen, emailMsg={}, disabled=false, ...props }) =
</div> </div>
)} )}
<Divider className='my-2' /> <Divider className='my-2' />
<div className={`mt-2 ${mailData.info?.MAI_ContentType === 'text/html' ? '' : 'whitespace-pre-wrap'}`} dangerouslySetInnerHTML={{ __html: mailData.content }}></div> {/* <div className={`mt-2 ${mailData.info?.MAI_ContentType === 'text/html' ? '' : '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>
</div> </div>
</DnDModal> </DnDModal>

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

Loading…
Cancel
Save