perf: 编辑器工具栏吸顶; fix: 邮件消息通过返回结果更新coli id, 从详情回复的邮件缺少coli id; 回复邮件含有引用时编辑器导入导致原文格式丢失

dev/emitter
Lei OT 10 months ago
parent 7132d7b76a
commit 9584d783f4

@ -766,7 +766,7 @@ export default function ToolbarPlugin() {
}, [editor]);
return (
<div className='toolbar' ref={toolbarRef}>
<div className='toolbar sticky top-[-10px]' ref={toolbarRef}>
<button type='button'
disabled={!canUndo}
onClick={() => {

@ -1,6 +1,6 @@
import { create } from 'zustand';
import { RealTimeAPI } from '@/channel/realTimeAPI';
import { olog, isEmpty, groupBy, sortArrayByOrder, logWebsocket } from '@/utils/commons';
import { olog, isEmpty, groupBy, sortArrayByOrder, logWebsocket, pick } from '@/utils/commons';
import { receivedMsgTypeMapped, handleNotification } from '@/channel/bubbleMsgUtils';
import { fetchConversationsList, fetchTemplates, fetchConversationsSearch, UNREAD_MARK, fetchTags } from '@/actions/ConversationActions';
import { devtools } from 'zustand/middleware';
@ -354,9 +354,12 @@ const messageSlice = (set, get) => ({
// * 已读的不再更新状态, 有时候投递结果在已读之后返回
// if (ele.id === ele.actionId && ele.actionId === message.actionId) {
if (ele.actionId === message.actionId) {
// console.log('actionID', message, ele)
// WABA: 同步返回, 根据actionId 更新消息的id
return { ...ele, id: message.id, status: ele.status === 'read' ? ele.status : message.status, dateString: message.dateString };
const toUpdateFields = pick(message, ['id', 'status', 'dateString', 'replyButton', 'coli_id', 'coli_sn']);
return { ...ele, ...toUpdateFields, status: ele.status === 'read' ? ele.status : message.status, };
} else if (String(ele.id) === String(message.id)) {
// console.log('id', message, ele)
// WABA: 异步的后续状态更新, id已更新为wamid
// console.log('old msg ele', ele);
const renderStatus = message?.data?.status ? { status: { ...ele.data.status, loading: 0, download: true } } : {};

@ -71,7 +71,7 @@ const BubbleEmail = ({ onOpenEditor, onOpenEmail, ...message }) => {
onForwardClick={ () => { handleResend(message.msgOrigin); }}
onOpen={() => handlePreview(message)}
onTitleClick={() => handlePreview(message)}
text={<RenderText str={message?.msgOrigin || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} email={message.msgOrigin} sender={message.sender} />}
text={<RenderText str={message?.msgOrigin || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} email={{...message.msgOrigin, coli_id: message.coli_id || message.msgOrigin?.coli_id}} sender={message.sender} />}
{...(message.sender === 'me'
? {
styles: { backgroundColor: '#e0e7ff', boxShadow: 'none', border: '1px solid #818cf8' }, // 100 400

@ -85,8 +85,8 @@ const EmailDetail = ({ open, setOpen, emailMsg={}, ...props }) => {
// ``
if (showBindBtn) {
btns.push(<EmailBindFormModal onBoundSuccess={() => setShowBindBtn(false)} {...{conversationid, mai_sn, showBindBtn}} />)
btns.push(<Divider type='vertical' key={'divider'} />);
btns.push(<EmailBindFormModal key={'bind'} onBoundSuccess={() => setShowBindBtn(false)} {...{conversationid, mai_sn, showBindBtn}} />)
btns.push(<Divider key='divider1' type='vertical' key={'divider'} />);
}
switch (status) {

@ -29,36 +29,7 @@ const getAbstract = (longtext) => {
return abstract;
};
const generateQuoteContent = (mailData) => `<br>
<hr>
<p>
<b>
<strong >From: </strong>
</b>
<span >${((mailData.info?.MAI_From || '').replace(/</g,'&lt;').replace(/>/g,'&gt;'))} </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 || '').replace(/</g,'&lt;').replace(/>/g,'&gt;')}</span>
</p>
<p>
<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>
`;
const generateQuoteContent = (mailData) => `<br><hr><p><b><strong >From: </strong></b><span >${((mailData.info?.MAI_From || '').replace(/</g,'&lt;').replace(/>/g,'&gt;'))} </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 || '').replace(/</g,'&lt;').replace(/>/g,'&gt;')}</span></p><p><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>`;
const generateMailContent = (mailData) => `<br><br>
<p>
@ -150,6 +121,8 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
setEmailMat(_findMatOld.mat_sn)
}
}
setShowQuoteContent(false);
setMergeQuote(true);
return () => {}
}, [open, mailData])
@ -189,6 +162,9 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
const [initialForm, setInitialForm] = useState({});
const [initialContent, setInitialContent] = useState('');
const [showQuoteContent, setShowQuoteContent] = useState(false);
const [mergeQuote, setMergeQuote] = useState(true);
const [quoteContent, setQuoteContent] = useState('');
useEffect(() => {
// console.log('quoteid', quoteid, isEmpty(quoteid), isEmpty(mailData.info));
@ -201,12 +177,12 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
const signatureBody = generateMailContent({ content: signature })
const preQuoteBody = generateQuoteContent(mailData)
// const preQuoteBody = generateQuoteContent(mailData)
const _initialContent = isEmpty(mailData.info) ? signatureBody : signatureBody+preQuoteBody
// const _initialContent = isEmpty(mailData.info) ? signatureBody : signatureBody+preQuoteBody
if (!isEmpty(mailData.info) && action !== 'edit') {
setInitialContent(_initialContent)
setInitialContent(signatureBody)
}
const _formValues = {
@ -395,7 +371,8 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
body.coli_sn = emailOrder || '';
// console.log('body', body, '\n', emailOrder);
const values = await form.validateFields();
body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent }) : textContent
const preQuoteBody = quoteContent ? quoteContent : generateQuoteContent(mailData);
body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent+preQuoteBody }) : textContent // +preQuoteBody
body.cc = values.cc || '';
body.bcc = values.bcc || '';
const msgObj = {
@ -458,9 +435,9 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
rootClassName='email-editor-wrapper !border-indigo-300 '
open={open}
setOpen={setOpen}
initial={{ width: window.innerWidth-600, height: window.innerHeight-40, left: 300 + 24, top: 20 }}
initial={{ width: window.innerWidth - 600, height: window.innerHeight - 40, left: 300 + 24, top: 20 }}
onCancel={() => {
form.resetFields();
form.resetFields()
stateReset()
}}
title={
@ -474,17 +451,16 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
<Button type='primary' onClick={onHandleSend} loading={sendLoading}>
发送
</Button>
<Popconfirm description='切换内容为纯文本格式将丢失信件和签名的格式, 确定使用纯文本?' onConfirm={confirmPlainText} open={openPlainTextConfirm} onCancel={() => setOpenPlainTextConfirm(false)}>
<Popconfirm
description='切换内容为纯文本格式将丢失信件和签名的格式, 确定使用纯文本?'
onConfirm={confirmPlainText}
open={openPlainTextConfirm}
onCancel={() => setOpenPlainTextConfirm(false)}>
<Checkbox checked={!isRichText} onChange={handlePlainTextOpenChange}>
纯文本
</Checkbox>
</Popconfirm>
<Select labelInValue
options={emailListOption}
value={{key: newFromEmail, value: newFromEmail, label: newFromEmail}}
onChange={handleSwitchEmail}
variant={'borderless'}
/>
<Select labelInValue options={emailListOption} value={{ key: newFromEmail, value: newFromEmail, label: newFromEmail }} onChange={handleSwitchEmail} variant={'borderless'} />
</div>
}>
<Form
@ -544,21 +520,19 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
<Form.Item label='主题' name={'subject'} rules={[{ required: true }]} className='w-full pt-1'>
<Input />
</Form.Item>
<Form.Item
name='atta'
label=''
className='w-full py-1 border-b-0'
valuePropName='fileList'
getValueFromEvent={normFile}
>
<Form.Item name='atta' label='' className='w-full py-1 border-b-0' valuePropName='fileList' getValueFromEvent={normFile}>
<Flex justify='space-between'>
<Upload {...uploadProps} name='file' className='w-full' >
<Upload {...uploadProps} name='file' className='w-full'>
<Button icon={<UploadOutlined />}>附件</Button>
</Upload>
<Flex align={'center'} className='absolute right-0'>
<Divider type='vertical' />
<Button type={'link'} onClick={() => openDrawerSnippet()}>图文集</Button>
<Button type={'link'} onClick={() => openPaymentDrawer()}>支付链接</Button>
<Button type={'link'} onClick={() => openDrawerSnippet()}>
图文集
</Button>
<Button type={'link'} onClick={() => openPaymentDrawer()}>
支付链接
</Button>
{/* 更多工具 */}
{/* <Popover
content={
@ -579,9 +553,22 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, oid,
</Form.Item>
</Form>
<LexicalEditor {...{ isRichText }} onChange={handleEditorChange} defaultValue={initialContent} />
{quoteid && !showQuoteContent && (
<div className='flex justify-start items-center ml-2'>
<Button className='flex gap-2 ' type='link' onClick={() => setShowQuoteContent(!showQuoteContent)}>
显示引用内容
</Button>
{/* <Button className='flex gap-2 ' type='link' danger onClick={() => {setMergeQuote(false);setShowQuoteContent(false)}}>
删除引用内容
</Button> */}
</div>
)}
{showQuoteContent && (
<div contentEditable className='border-0 outline-none' onBlur={(e) => setQuoteContent(e.target.innerHTML)} dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></div>
)}
</DnDModal>
</ConfigProvider>
</>
);
)
};
export default EmailEditorPopup;

Loading…
Cancel
Save