feat: 获取邮件详情, 查看与回复

2.0/email-builder
Lei OT 11 months ago
parent b682202d0c
commit d1c1b7df6f

@ -557,9 +557,10 @@ export const whatsappMsgTypeMapped = {
type: 'email',
data: (msg) => ({ id: msg.id || msg.uid, subject: msg.subject, }),
renderForReply: (msg) => {
const _msg = { ...msg, ...msg.email };
return {
id: msg.id,
message: `[邮件] ${msg.subject}`,
id: _msg.id,
message: `[邮件] ${_msg.subject}`,
};
},
},
@ -643,7 +644,7 @@ export const parseRenderMessageList = (messages) => {
}
return {
...msg,
msgOrigin: msgContent,
msgOrigin: {...msgContent, ...msgContent.email},
...(whatsappMsgTypeMapped?.[msgType]?.data(msgContent) || {}),
type: msgContent.type,
...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }),

@ -0,0 +1,32 @@
import { useState, useEffect } from 'react'
import { isEmpty } from '@/utils/commons'
import { getEmailDetailAction } from '@/actions/EmailActions'
/**
* @param mai_sn 邮件编号ID
* @param data 直接传递, 不重复获取
* * 在详情点击`回复`呼出编辑时
*/
export const useEmailDetail = (mai_sn, data) => {
const [loading, setLoading] = useState(false)
const [mailData, setMailData] = useState({ loading, info: {}, content: '', attachments: [] })
useEffect(() => {
const getEmailDetail = async () => {
if (isEmpty(mai_sn)) {
return false
}
try {
setLoading(true)
const data = await getEmailDetailAction({ mai_sn })
setMailData(data)
setLoading(false)
} catch (err) {
setLoading(false)
}
}
if (isEmpty(data)) getEmailDetail()
else setMailData(data)
}, [mai_sn])
return mailData
}

@ -6,6 +6,7 @@ import EmailEditorPopup from '../Input/EmailEditorPopup'
import DnDModal from '@/components/DndModal'
import useStyleStore from '@/stores/StyleStore'
import { getEmailDetailAction } from '@/actions/EmailActions'
import { useEmailDetail } from '@/hooks/useEmail';
const TagColorStyle = (tag) => {
const color = stringToColour(tag)
@ -17,12 +18,10 @@ const TagColorStyle = (tag) => {
}
const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
// console.log('emailMsg', emailMsg);
let { conversationid, } = emailMsg
// const { mai_sn } = emailMsg.msgtext?.email || {}
const mai_sn = 5036841 // debug: 0
// const [open, setOpen] = useState(false);
let { conversationid, order_opi } = emailMsg
const { mai_sn, id } = emailMsg.msgOrigin?.email || emailMsg.msgOrigin || {}
const mailID = mai_sn || id
const [initialPosition, setInitialPosition] = useState({})
const [initialSize, setInitialSize] = useState({})
function onHandleMove(e) {
@ -40,45 +39,30 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
const [openEmailEditor, setOpenEmailEditor] = useState(false)
const [fromEmail, setFromEmail] = useState('')
const [ReferEmailMsg, setReferEmailMsg] = useState('')
const onOpenEditor = (magtext, action) => {
const { from } = magtext
const onOpenEditor = (msgOrigin, action) => {
const { from } = msgOrigin
setOpenEmailEditor(true)
setFromEmail(from)
setReferEmailMsg(magtext)
setReferEmailMsg(msgOrigin)
setAction(action)
setOpen(false)
}
const [mobile] = useStyleStore((state) => [state.mobile])
const [mailData, setMailData] = useState({info: {}, content: '', attachments: []})
const getEmailDetail = async () => {
if (isEmpty(mai_sn)) {
return false;
}
const data = await getEmailDetailAction({ mai_sn })
console.log('data', data);
setMailData(data)
}
useEffect(() => {
getEmailDetail()
return () => {}
}, [emailMsg])
const mailData = useEmailDetail(mailID)
const ActionBtns = (props) => (
<div className={`flex items-center w-full ${props.className || ''}`}>
<Button
onClick={() => onOpenEditor(emailMsg.msgtext, 'reply')}
onClick={() => onOpenEditor(emailMsg.msgOrigin, 'reply')}
size='small'
type='text'
icon={<ReplyIcon className='text-indigo-500' />}>
回复
</Button>
<Button
onClick={() => onOpenEditor(emailMsg.msgtext, 'forward')}
onClick={() => onOpenEditor(emailMsg.msgOrigin, 'forward')}
size='small'
type='text'
icon={<ShareForwardIcon className='text-primary' />}>
@ -92,14 +76,15 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
<DnDModal
open={open}
setOpen={setOpen}
title={mailData.info?.subject || emailMsg?.msgtext?.email?.subject}
title={mailData.info?.subject || emailMsg?.msgOrigin?.email?.subject}
initial={{ top: 74 }}
onMove={onHandleMove}
onResize={onHandleResize}
footer={mobile ? <ActionBtns className='w-full' /> : null}>
footer={mobile ? <ActionBtns className='w-full' /> : null}
>
<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?.subject || emailMsg?.msgtext?.email?.subject}</div>
<div className=' font-bold'>{mailData.info?.subject || emailMsg?.msgOrigin?.email?.subject}</div>
<div>
<div
@ -110,7 +95,6 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
<div className='flex gap-2 mb-2 items-center'>
<Avatar
className=''
// style={TagColorStyle(emailOrigin.fromEmail)}>
style={TagColorStyle(mailData.info?.MAI_From)}>
{(mailData.info?.MAI_From || '').substring(0, 1)}
</Avatar>
@ -125,16 +109,12 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
</div>
<div className='flex flex-col justify-start gap-1 items-end'>
<ActionBtns />
{/* <div>{emailDetail.dateText}</div> */}
<div className='text-xs '>{emailMsg.localDate}</div>
</div>
</div>
<div className='text-sm'>
<span className='text-neutral-500 pr-2'>收件人:</span>
{mailData.info?.MAI_To}
{/* <span className='text-neutral-600'>
&nbsp;&nbsp;&lt;{mailData.info?.toEmail}&gt;
</span> */}
</div>
{mailData.info?.cc && (
<div className='text-sm'>
@ -148,10 +128,6 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
{mailData.info.bcc}
</div>
)}
{/* <div className='text-sm'>
<span className='text-neutral-500 pr-2'>主题:</span>
{emailOrigin.subject}
</div> */}
{mailData.attachments.length > 0 && (
<div className='mt-2 *:ml-2'>
<span>{mailData.attachments.length}个附件</span>
@ -161,12 +137,9 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
</div>
)}
<Divider className='my-2' />
{/* <div className='mt-2'>{emailOrigin.body}</div> */}
<div
className='mt-2'
// mailData
dangerouslySetInnerHTML={{ __html: mailData.content }}></div>
{/* <div className='mt-2'>{mailData.attachments.map(attachment => <div key={attachment.name}>{attachment.name}</div>)}</div> */}
</div>
</div>
</DnDModal>
@ -174,10 +147,12 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
open={openEmailEditor}
setOpen={setOpenEmailEditor}
fromEmail={fromEmail}
fromUser={mailData.info?.MAI_OPI_SN}
conversationID={conversationid}
quote={ReferEmailMsg}
fromUser={mailData.info?.MAI_OPI_SN || order_opi}
conversationid={conversationid}
// emailMsg={ReferEmailMsg}
quoteid={mailID}
initial={{ ...initialPosition, ...initialSize }}
mailData={mailData}
action={action}
key={`email-editor-inner-${action}-popup_${ReferEmailMsg.id}`}
/>

@ -1,7 +1,6 @@
import { createContext, useEffect, useState } from 'react';
import { ConfigProvider, Button, Form, Input, Flex, Checkbox, Switch, Mentions, Popover, Popconfirm, Select, Space, Upload, Divider } from 'antd';
import { DashOutlined, EllipsisOutlined, MenuOutlined, MoreOutlined, UploadOutlined } from '@ant-design/icons';
import Modal from '@dckj/react-better-modal';
import { useEffect, useState } from 'react';
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import '@dckj/react-better-modal/dist/index.css';
import DnDModal from '@/components/DndModal';
import useStyleStore from '@/stores/StyleStore';
@ -14,7 +13,8 @@ import { v4 as uuid } from 'uuid';
import { isEmpty } from '@/utils/commons';
import './EmailEditor.css';
import { postSendEmail } from '@/actions/EmailActions';
import { sentMsgTypeMapped, whatsappSupportFileTypes, uploadProgressSimulate } from '@/channel/bubbleMsgUtils';
import { sentMsgTypeMapped, } from '@/channel/bubbleMsgUtils';
import { useEmailDetail } from '@/hooks/useEmail';
const getAbstract = (longtext) => {
const lines = longtext.split('\n');
@ -23,11 +23,48 @@ const getAbstract = (longtext) => {
return abstract;
};
const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote = {}, initial = {}, action = 'reply', ...props }) => {
const generateQuoteContent = (mailData) => `<br><br>
<hr>
<p>
<b>
<strong >From: </strong>
</b>
<span >${mailData.info?.MAI_From || ''} </span>
</p>
<p>
<b>
<strong >Sent: </strong>
</b>
<span >${mailData.info?.MAI_SendDate || ''}</span>
</p>
<p>
<b>
<strong >To: </strong>
</b>
<span >${mailData.info?.MAI_To || ''}</span>
</p>
<p>
<b>
<strong >Subject: </strong>
</b>
<span >${mailData.info.subject || ''}</span>
</p>
<p>
${mailData.content}
</p>
`;
const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, conversationid, quoteid, initial = {}, mailData: _mailData, action = 'reply', ...props }) => {
const [mobile] = useStyleStore((state) => [state.mobile]);
const [userId, username, emailList] = useAuthStore((state) => [state.loginUser.userId, state.loginUser.username, state.loginUser.emailList]);
const emailListOption = emailList?.map(ele => ({ ...ele, label: ele.email, key: ele.email, value: ele.email })) || [];
const mai_sn = quoteid;
const mailData = useEmailDetail(mai_sn, _mailData);
const emailUser = mailData.info?.MAI_OPI_SN || fromUser // quote.order_opi
const { notification, message } = App.useApp();
const [form] = Form.useForm();
const [isRichText, setIsRichText] = useState(mobile === false);
@ -56,51 +93,31 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
const [newFromEmail, setNewFromEmail] = useState('');
const [initialForm, setInitialForm] = useState({});
const [initialContent, setInitialContent] = useState('');
/**
* 加载邮件数据
*/
useEffect(() => {
if (isEmpty(quote)) {
// console.log('quoteid', quoteid, isEmpty(quoteid), isEmpty(mailData.info));
if (isEmpty(quoteid)) {
return () => {};
}
setShowCc(!isEmpty(quote.cc));
const { from, email: { subject, content, mai_sn } } = quote;
const preQuoteBody = `<br><br>
<hr>
<p>
<b>
<strong >From: </strong>
</b>
<span >${quote.from} </span>
</p>
<p>
<b>
<strong >Sent: </strong>
</b>
<span >${quote.sendTime || ''}</span>
</p>
<p>
<b>
<strong >To: </strong>
</b>
<span >${quote.to}</span>
</p>
<p>
<b>
<strong >Subject: </strong>
</b>
<span >${subject}</span>
</p>
<p>
${content}
</p>
`;
const { info, } = mailData
setShowCc(!isEmpty(mailData.info?.MAI_CS));
const preQuoteBody = generateQuoteContent(mailData);
if ( !isEmpty(mailData.info))
setInitialContent(preQuoteBody);
setInitialContent(preQuoteBody);
const _formValues = {
to: from || fromEmail,
cc: quote.cc || '',
bcc: quote.bcc || '',
subject: `Re: ${subject}`,
to: info?.MAI_From || fromEmail,
cc: info?.MAI_CS || '',
// bcc: quote.bcc || '',
subject: `Re: ${info.subject || ''}`,
};
const forwardValues = { subject: `Fw: ${subject}` };
const forwardValues = { subject: `Fw: ${info.subject || ''}` };
if (action === 'reply') {
form.setFieldsValue(_formValues);
setInitialForm(_formValues);
@ -110,7 +127,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
}
return () => {};
}, [quote, open]);
}, [open, mailData.info]);
const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false);
const handlePlainTextOpenChange = ({ target }) => {
@ -181,7 +198,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
status: 'waiting',
...msgObj,
// id: `${currentConversation.sn}.${msgObj.id}`,
id: `1148.${msgObj.id}`,
id: `${conversationid}.${msgObj.id}`,
};
// olog('invoke upload', msgObjMerge)
const contentToRender = sentMsgTypeMapped[msgObjMerge.type].contentToRender(msgObjMerge);
@ -197,9 +214,10 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
const body = structuredClone(form.getFieldsValue());
body.mailcontent = isRichText ? htmlContent : textContent;
body.from = newFromEmail || fromEmail;
body.to = 'lyt@hainatravel.com'; // debug: 0
body.attaList = fileList;
body.opi_sn = fromUser;
body.mat_sn = 278;
body.opi_sn = emailUser || fromUser;
body.mat_sn = 278; // todo:
console.log('body', body);
const values = await form.validateFields();
const msgObj = {
@ -214,12 +232,25 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
content: body.mailcontent,
},
}
invokeEmailMessage(msgObj);
setSendLoading(true);
const result = await postSendEmail(body);
body.externalID = conversationid;
body.actionID = `${conversationid}.${msgObj.id}`;
try {
const result = await postSendEmail(body);
setSendLoading(false);
invokeEmailMessage(msgObj);
setOpen(false);
} catch (error) {
notification.error({
message: "邮件保存失败",
// description: error,
placement: "top",
// duration: 60,
});
}
setSendLoading(false);
setOpen(false);
};
return (
@ -229,11 +260,11 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, reference, quote
rootClassName='email-editor-wrapper !border-indigo-300 '
open={open}
setOpen={setOpen}
initial={{ top: isEmpty(reference) ? 20 : 74 }}
initial={{ top: isEmpty(quoteid) ? 20 : 74 }}
onCancel={() => {
form.resetFields();
}}
title={initialForm.subject || `${isEmpty(quote) ? '回复: ' : '写邮件: '} ${fromEmail || ''}`}
title={initialForm.subject || `${isEmpty(quoteid) ? '回复: ' : '写邮件: '} ${fromEmail || ''}`}
footer={
<div className='w-full flex gap-6 justify-start items-center text-indigo-600'>
<Button type='primary' onClick={onHandleSend} loading={sendLoading}>

@ -12,6 +12,7 @@ import emailReItem from './Components/emailRe.json';
// import EmailEditor from './Input/bak/EmailEditor';
import EmailEditorPopup from './Input/EmailEditorPopup';
import EmailDetail from './Components/EmailDetail';
import { useOrderStore, } from "@/stores/OrderStore";
const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const userId = useAuthStore((state) => state.loginUser.userId);
@ -25,6 +26,10 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const setMsgLoading = useConversationStore((state) => state.setMsgLoading);
const refreshTotalNotify = useConversationStore(useShallow((state) => state.refreshTotalNotify));
const { orderDetail, } = useOrderStore();
const isVisible = useVisibilityState();
const [longList, setLongList] = useState([]);
@ -68,10 +73,6 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const loadNextPage = !(data.length === 0 || data.length < MESSAGE_PAGE_SIZE);
updateCurrentConversation({ lasttime: thisLastTime, loadNextPage });
// test: 0
// data.push(emailItem);
// data.push(emailReItem);
setMsgLoading(false);
receivedMessageList(item.sn, data);
};
@ -146,14 +147,14 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
setOpenEmailEditor(true);
setFromEmail(from);
setReferEmailMsg(emailMsgContent);
setReferEmailMsg({...emailMsgContent, order_opi: Number(orderDetail?.opi_sn || userId)});
};
const [openEmailDetail, setOpenEmailDetail] = useState(false);
const [emailDetail, setEmailDetail] = useState({});
const onOpenEmail = (emailMsg) => {
setOpenEmailDetail(true);
setEmailDetail(emailMsg);
setEmailDetail({...emailMsg, order_opi: Number(orderDetail?.opi_sn || userId)});
}
return (
@ -205,7 +206,7 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
onCancel={() => setNewChatModalVisible(false)}
/>
{/* <EmailEditor open ={openEmailEditor} setOpen={setOpenEmailEditor} reference={ReferEmailMsg} setRefernce={setReferEmailMsg} {...{ fromEmail, }} key={'email-editor-reply'} /> */}
<EmailEditorPopup open={openEmailEditor} setOpen={setOpenEmailEditor} fromEmail={fromEmail} quote={ReferEmailMsg} key={`email-editor-reply-top-popup_${ReferEmailMsg.id}`} />
<EmailEditorPopup open={openEmailEditor} setOpen={setOpenEmailEditor} fromEmail={fromEmail} quoteid={ReferEmailMsg.mai_sn || ReferEmailMsg.id} key={`email-editor-reply-top-popup_${ReferEmailMsg.id}`} />
<EmailDetail open={openEmailDetail} setOpen={setOpenEmailDetail} emailMsg={emailDetail} key={`email-detail-${emailDetail.id}`} />
</>
);

Loading…
Cancel
Save