import { useEffect, useState, useRef, useCallback } from 'react';
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs } 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, debounce, isEmpty, } from '@/utils/commons';
import { writeIndexDB } from '@/utils/indexedDB';
import './EmailEditor.css';
import { postSendEmail } from '@/actions/EmailActions';
import { sentMsgTypeMapped, } from '@/channel/bubbleMsgUtils';
import { EmailBuilder, useEmailDetail, useEmailSignature } from '@/hooks/useEmail';
import useSnippetStore from '@/stores/SnippetStore'
import { useOrderStore } from '@/stores/OrderStore'
import PaymentlinkBtn from './PaymentlinkBtn';
// 禁止上传的附件类型
// .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 parseHTMLText = (html) => {
const parser = new DOMParser()
const dom = parser.parseFromString(html, 'text/html')
// Replace
and
with line breaks Array.from(dom.body.querySelectorAll('br, p')).forEach(el => { el.textContent = '\n' + el.textContent; }); // Replace
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.info?.MAI_ContentType === 'text/html' ? mailData.content : mailData.content.replace(/\r\n/g, '
')}
${mailData.content}
` /** * @property {string} fromEmail - 发件人邮箱 * @property {string} fromUser - 发件人用户 * @property {string} fromOrder - 发件订单 * @property {string} toEmail - 收件人邮箱 * @property {string} conversationid - 会话ID * @property {string} quoteid - 引用邮件ID * @property {object} draft - 草稿 */ const EmailEditorPopup = () => { 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 emailEdiorProps = useConversationStore((state) => state.emailEdiorProps); const [open, setOpen, closeEditor1, currentEditKey, setCurrentEditKey] = useConversationStore((state) => [state.editorOpen, state.setEditorOpen, state.closeEditor1, state.currentEditKey, state.setCurrentEditKey]); const propsKeysArr = Array.from(emailEdiorProps.keys()); const propsArr = Array.from(emailEdiorProps.values()); const [activeEdit, setActiveEdit] = useState(emailEdiorProps.get(currentEditKey) || {}); // const { fromEmail, fromUser, fromOrder, oid, toEmail, conversationid, quoteid, initial = {}, mailData: _mailData, action = 'reply', draft = {}, receiverName, ...props } = emailEdiorProps.get(currentEditKey) || {}; const onChangeActiveEditor = (key) => { setCurrentEditKey(key); const _find = emailEdiorProps.get(key) || {}; setActiveEdit(_find); }; const onEditTab = (targetKey, action) => { if (action === 'add') { // } else { if (propsKeysArr.length === 1) { setOpen(false); } closeEditor1(targetKey); } }; const mai_sn = activeEdit.quoteid; const { loading: getLoading, mailData } = useEmailDetail(mai_sn, activeEdit.mailData) const [stickToProps, setStickToProps] = useState({}); const [propsSerialize, setPropsSerialize] = useState(''); const [newFromEmail, setNewFromEmail] = useState(''); const [newToEmail, setNewToEmail] = useState(''); const [emailOPI, setEmailOPI] = useState(''); const [emailOrder, setEmailOrder] = useState(''); const [emailMat, setEmailMat] = useState(''); const stateReset = () => { setStickToProps({}) setStickToCid('') setEmailOrder('') setEmailOPI('') setNewFromEmail('') setNewToEmail('') } const [contentPrefix, setContentPrefix] = useState(''); // 存储: 会话ID, // 这个窗口没有模态, 即使不是focus, 还是需要保持会话ID // 否则, 会话列表切换之后, 会话ID更新, 导致消息关联错误 const [stickToCid, setStickToCid] = useState(activeEdit.conversationid); useEffect(() => { const propsObj = { ...activeEdit, mai: activeEdit.mailData?.info?.MAI_MAT_SN, } setContentPrefix(activeEdit.oid ? `Dear Mr./Ms. ${activeEdit.receiverName || ''}
Reference Number: ${activeEdit.oid}
` : ''); // 没有引用邮件 if (isEmpty(activeEdit.quoteid)) { setStickToProps(propsObj) setPropsSerialize(JSON.stringify(propsObj)) setStickToCid(activeEdit.conversationid) setEmailOrder(activeEdit.fromOrder) setEmailOPI(activeEdit.fromUser) setNewFromEmail(activeEdit.fromEmail) setNewToEmail(activeEdit.toEmail) const _findMat = emailListAddrMapped?.[activeEdit.fromEmail]?.mat_sn setEmailMat(_findMat) // if (open !== true) { // form.resetFields() // } } // 转发/回复时, 使用详情的账户信息 if (mailData.info?.MAI_MAT_SN) { 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 || activeEdit.fromOrder) 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) } } setShowQuoteContent(false); setMergeQuote(true); setQuoteContent('') return () => {} }, [open, mailData]) const handleSwitchEmail = (labelValue) => { const { value } = labelValue setNewFromEmail(value) const _findMat = emailListAddrMapped?.[value] setEmailMat(_findMat?.mat_sn) setEmailOPI(_findMat?.opi_sn) }; const { signature } = useEmailSignature(emailOPI) 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)); debouncedSave({htmlContent}); }; 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)); if (isEmpty(activeEdit.quoteid) && activeEdit.action !== 'new') { return () => {} } const { info } = mailData // setShowCc(!isEmpty(mailData.info?.MAI_CS)); const signatureBody = generateMailContent({ content: signature }) // const preQuoteBody = generateQuoteContent(mailData) // const _initialContent = isEmpty(mailData.info) ? signatureBody : signatureBody+preQuoteBody if (!isEmpty(mailData.info) && activeEdit.action !== 'edit') { setInitialContent(contentPrefix + signatureBody) } const _formValues = { to: info?.replyToAll || newFromEmail, cc: info?.MAI_CS || '', // bcc: quote.bcc || '', subject: `Re: ${info.MAI_Subject || ''}`, } const forwardValues = { from: newFromEmail, subject: `Fw: ${info.MAI_Subject || ''}` } if (activeEdit.action === 'reply') { form.setFieldsValue(_formValues) setInitialForm(_formValues) } else if (activeEdit.action === 'forward') { setStickToCid('0') form.setFieldsValue(forwardValues) setInitialForm(forwardValues) } else if (activeEdit.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 (activeEdit.action === 'new') { const newEmail = { to: newToEmail, subject: activeEdit.draft?.subject || '' } form.setFieldsValue(newEmail) setInitialForm(newEmail) setInitialContent((activeEdit.draft?.content || contentPrefix || '') + signatureBody) } return () => {} }, [propsSerialize, mailData.info, signature, 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); }, onPreview: (file) => { // console.log('pn preview', file); return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = (e) => { if (file.size > 1.5 * 1024 * 1024) { message.info('附件太大,无法预览') // message.info('附件太大,无法预览, 请下载后查看') // var downloadLink = document.createElement('a'); // downloadLink.href = e.target.result; // downloadLink.download = file.name; // downloadLink.click(); resolve(e.target.result); return; } var win = window.open("", "_blank"); win.document.body.style.margin = '0'; if (file.type.startsWith('image/')) { win.document.write("setQuoteContent(`)}${e.target.innerHTML}`)} dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}>