import { useEffect, useState } from 'react'; import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider } from 'antd'; import { UploadOutlined, LoadingOutlined } from '@ant-design/icons'; import '@dckj/react-better-modal/dist/index.css'; import DnDModal from '@/components/DndModal'; import useStyleStore from '@/stores/StyleStore'; import useConversationStore from '@/stores/ConversationStore'; import useAuthStore from '@/stores/AuthStore'; import LexicalEditor from '@/components/LexicalEditor'; import { v4 as uuid } from 'uuid'; import { cloneDeep, isEmpty } from '@/utils/commons'; import './EmailEditor.css'; import { postSendEmail } from '@/actions/EmailActions'; import { sentMsgTypeMapped, } from '@/channel/bubbleMsgUtils'; import { useEmailDetail } from '@/hooks/useEmail'; import useSnippetStore from '@/stores/SnippetStore' import { useOrderStore } from '@/stores/OrderStore' // 禁止上传的附件类型 // .application, .exe, .app const disallowedAttachmentTypes = ['.ps1','.msi','application/x-msdownload', 'application/x-ms-dos-executable', 'application/x-ms-wmd', 'application/x-ms-wmz', 'application/x-ms-xbap', 'application/x-msaccess', ]; const getAbstract = (longtext) => { const lines = longtext.split('\n'); const firstLine = lines[0]; const abstract = firstLine.substring(0, 20); return abstract; }; const generateQuoteContent = (mailData) => `


From: ${((mailData.info?.MAI_From || '').replace(//g,'>'))}

Sent: ${mailData.info?.MAI_SendDate || ''}

To: ${(mailData.info?.MAI_To || '').replace(//g,'>')}

Subject: ${mailData.info?.MAI_Subject || ''}

${mailData.content}

`; const generateMailContent = (mailData) => `

${mailData.content}

` /** * @property {string} fromEmail - 发件人邮箱 * @property {string} fromUser - 发件人用户 * @property {string} fromOrder - 发件人订单 * @property {string} toEmail - 收件人邮箱 * @property {string} conversationid - 会话ID * @property {string} quoteid - 引用邮件ID */ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, toEmail, conversationid, quoteid, initial = {}, mailData: _mailData, action = 'reply', ...props }) => { const { notification, message } = App.useApp(); const [form] = Form.useForm(); 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 emailListMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.opi_sn]: v }), {}); const emailListAddrMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.email]: v }), {}); const emailListMatMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.mat_sn]: v }), {}); // console.log('emailListMapped', emailListOption, emailListAddrMapped); const mai_sn = quoteid; const { loading: getLoading, mailData } = useEmailDetail(mai_sn, _mailData) const [stickToProps, setStickToProps] = useState({}); const [newFromEmail, setNewFromEmail] = useState(''); const [newToEmail, setNewToEmail] = useState(''); const [emailOPI, setEmailOPI] = useState(''); const [emailOrder, setEmailOrder] = useState(''); const [emailMat, setEmailMat] = useState(''); // 存储: 会话ID, // 这个窗口没有模态, 即使不是focus, 还是需要保持会话ID // 否则, 会话列表切换之后, 会话ID更新, 导致消息关联错误 const [stickToCid, setStickToCid] = useState(conversationid); useEffect(() => { // console.log('emailEditorPopup effect', open, '\nto', toEmail) setStickToProps({ fromEmail, fromUser, fromOrder, toEmail, conversationid, quoteid, action }); setStickToCid(conversationid) setEmailOrder(fromOrder) setEmailOPI(fromUser) setNewFromEmail(fromEmail) setNewToEmail(toEmail) const _findMat = emailListAddrMapped?.[fromEmail]?.mat_sn setEmailMat(_findMat) if (open !== true) { form.resetFields() } return () => { setStickToProps({}) setStickToCid('') setEmailOrder('') setEmailOPI('') setNewFromEmail('') setNewToEmail('') } }, [open]) // 转发/回复时, 使用详情的账户信息 useEffect(() => { const reEmailO = mailData.info?.MAI_COLI_SN const reEmailUser = mailData.info?.MAI_OPI_SN const reEmailUserMat = mailData.info?.MAI_MAT_SN setEmailOrder(prev => reEmailO || prev); setEmailOPI(prev => reEmailUser || prev); setEmailMat(prev => reEmailUserMat || prev) const _findMatOld = emailListMatMapped?.[reEmailUserMat] if (_findMatOld) { setNewFromEmail(_findMatOld.email) setEmailOPI(_findMatOld.opi_sn) setEmailMat(_findMatOld.mat_sn) } return () => {} }, [mailData]) const handleSwitchEmail = (labelValue) => { const { value } = labelValue setNewFromEmail(value) const _findMat = emailListAddrMapped?.[value] setEmailMat(_findMat?.mat_sn) setEmailOPI(_findMat?.opi_sn) }; const [isRichText, setIsRichText] = useState(mobile === false); // const [isRichText, setIsRichText] = useState(false); // 默认纯文本 const [htmlContent, setHtmlContent] = useState(''); const [textContent, setTextContent] = useState(''); const [showCc, setShowCc] = useState(true); const [showBcc, setShowBcc] = useState(false); const handleShowCc = () => { setShowCc(true); }; const handleShowBcc = () => { setShowBcc(true); }; const handleEditorChange = ({ editorStateJSON, htmlContent, textContent }) => { // console.log('textContent', textContent); // console.log('html', html); setHtmlContent(htmlContent); setTextContent(textContent); form.setFieldValue('content', htmlContent); form.setFieldValue('abstract', getAbstract(textContent)); }; const [initialForm, setInitialForm] = useState({}); const [initialContent, setInitialContent] = useState(''); useEffect(() => { // console.log('quoteid', quoteid, isEmpty(quoteid), isEmpty(mailData.info)); if (isEmpty(quoteid) && action !== 'new') { return () => {}; } const { info, } = mailData // setShowCc(!isEmpty(mailData.info?.MAI_CS)); setShowCc(true); const preQuoteBody = generateQuoteContent(mailData); if ( !isEmpty(mailData.info) && action !== 'edit') { setInitialContent(preQuoteBody); } const _formValues = { to: info?.MAI_From || newFromEmail, cc: info?.MAI_CS || '', // bcc: quote.bcc || '', subject: `Re: ${info.MAI_Subject || ''}`, }; const forwardValues = { subject: `Fw: ${info.MAI_Subject || ''}` }; if (action === 'reply') { form.setFieldsValue(_formValues); setInitialForm(_formValues); } else if (action === 'forward') { form.setFieldsValue(forwardValues); setInitialForm(forwardValues); } else if (action === 'edit') { const thisFormValues = { to: info?.MAI_To || '', cc: info?.MAI_CS || '', subject: info?.MAI_Subject || '', }; form.setFieldsValue(thisFormValues); setInitialForm(thisFormValues); const thisBody = generateMailContent(mailData); // console.log('thisBody', thisBody); setInitialContent(thisBody) } else if (action === 'new') { const newEmail = { to: newToEmail, } form.setFieldsValue(newEmail); setInitialForm(newEmail); } return () => {}; }, [stickToProps, mailData.info, newToEmail, newFromEmail]); const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false); const handlePlainTextOpenChange = ({ target }) => { const { checked: newChecked } = target; if (!newChecked) { setIsRichText(true); setOpenPlainTextConfirm(false); return; } setOpenPlainTextConfirm(true); }; const confirmPlainText = () => { setIsRichText(false); setOpenPlainTextConfirm(false); }; // 附件: // 1. ~直接上传返回地址~ // 2. 发送文件信息 const [fileList, setFileList] = useState([]); const handleChange = (info) => { let newFileList = [...info.fileList]; // 2. Read from response and show file link newFileList = newFileList.map((file) => { if (file.response) { // Component will show file.url as link file.url = file.response.url; } return file; }); setFileList(newFileList); }; const normFile = (e) => { // console.log('Upload event:', e); if (Array.isArray(e)) { return e; } return e?.fileList; }; const uploadProps = { // action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload', // onChange: handleChange, multiple: true, fileList, beforeUpload: (file) => { // console.log('beforeUpload', file); const lastDotIndex = file.name.lastIndexOf('.'); const extension = file.name.slice(lastDotIndex).toLocaleLowerCase(); if (disallowedAttachmentTypes.includes(file.type) || disallowedAttachmentTypes.includes(extension)) { message.warning('不支持的文件格式: '+ extension) return false; } setFileList(prev => [...prev, file]); return false; // 阻止默认上传, 附件不上传阿里云 }, onRemove: (file) => { const index = fileList.indexOf(file); const newFileList = fileList.slice(); newFileList.splice(index, 1); setFileList(newFileList); }, }; /** * 先推到消息记录上面, 再发送 */ const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage); const invokeEmailMessage = (msgObj) => { const msgObjMerge = { sender: 'me', senderName: 'me', msg_source: 'email', // to: currentConversation.whatsapp_phone_number, date: new Date(), status: 'waiting', // accepted ...msgObj, // id: `${currentConversation.sn}.${msgObj.id}`, // id: `${stickToCid}.${msgObj.id}`, conversationid: stickToCid, }; // olog('invoke upload', msgObjMerge) const contentToRender = sentMsgTypeMapped[msgObjMerge.type].contentToRender(msgObjMerge); // console.log(contentToRender, 'contentToRender sendMessage------------------'); sentOrReceivedNewMessage(contentToRender.conversationid, contentToRender); }; const [sendLoading, setSendLoading] = useState(false); const onHandleSend = async () => { // console.log('onSend callback', '\nisRichText', isRichText); // console.log(form.getFieldsValue()); const body = structuredClone(form.getFieldsValue()); body.mailcontent = isRichText ? htmlContent : textContent; body.from = newFromEmail; body.attaList = fileList; body.opi_sn = emailOPI; body.mat_sn = emailMat; body.coli_sn = emailOrder; // console.log('body', body, '\n', emailOrder); const values = await form.validateFields(); body.cc = values.cc || ''; body.bcc = values.bcc || ''; const msgObj = { type: 'email', id: uuid(), from: body.from, to: values.to, cc: values.cc || '', bcc: values.bcc || '', subject: values.subject, content: body.mailcontent, email: { subject: values.subject, content: body.mailcontent, }, } setSendLoading(true); body.externalID = stickToCid; body.actionID = `${stickToCid}.${msgObj.id}`; try { const bubbleMsg = cloneDeep(msgObj); bubbleMsg.id = `${stickToCid}.${msgObj.id}`; // bubbleMsg.email.mai_sn = ''; bubbleMsg.content = undefined; // invokeEmailMessage(bubbleMsg); const result = await postSendEmail(body); const mailSavedId = result.id || ''; bubbleMsg.email.mai_sn = mailSavedId; invokeEmailMessage(bubbleMsg); // setSendLoading(false); setOpen(false); } catch (error) { notification.error({ message: "邮件保存失败", description: error.message, placement: "top", duration: 3, }); } finally { setSendLoading(false); } }; const [openDrawerSnippet] = useSnippetStore((state) => [state.openDrawer]) const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer]) return ( <> } initialStage={'FULLSCREEN'} onCancel={() => { form.resetFields(); }} title={ <> {getLoading ? : null} {initialForm.subject || `${!isEmpty(quoteid) ? '回复: ' : '写邮件: '} ${newFromEmail || ''}`} } footer={
setOpenPlainTextConfirm(false)}> 纯文本 {!showCc && ( )} {!showBcc && ( )} {/* {!showCc && ( )} {!showBcc && ( )} } /> */} {/* todo: 添加: 支付链接 */} {/* 更多工具 */} {/*
} trigger='click' > */}
); }; export default EmailEditorPopup;