新窗口: 邮件编辑

dev/ckeditor
Lei OT 4 months ago
parent 92e5cab823
commit 41948fed6f

@ -19,7 +19,8 @@ export const getSalesSignatureAction = async (params) => {
try { try {
const { result } = await fetchJSON(`${EMAIL_HOST}/email_sign`, params) const { result } = await fetchJSON(`${EMAIL_HOST}/email_sign`, params)
const { SignContent: html } = result const { SignContent: html } = result
return parseHTMLString(html); const bodyContent = parseHTMLString(html);
return bodyContent;
} catch (error) { } catch (error) {
return ''; return '';
} }

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 6V21H11V6H5V4H19V6H13Z"></path></svg>

After

Width:  |  Height:  |  Size: 130 B

@ -1,7 +1,7 @@
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import ReplyLineSVG from '@/assets/icons/reply-line.svg?react'; import ReplyLineSVG from '@/assets/icons/reply-line.svg?react';
import ReplyAllLineSVG from '@/assets/icons/reply-all-line.svg?react'; import ReplyAllLineSVG from '@/assets/icons/reply-all-fill.svg?react';
import AttachmentLineSVG from '@/assets/icons/attachment-line.svg?react'; import AttachmentLineSVG from '@/assets/icons/attachment-line.svg?react';
import AttachmentFillSVG from '@/assets/icons/attachment-fill.svg?react'; import AttachmentFillSVG from '@/assets/icons/attachment-fill.svg?react';
// import ShareForwardFillSVG from '@/assets/icons/share-forward-fill.svg?react'; // import ShareForwardFillSVG from '@/assets/icons/share-forward-fill.svg?react';
@ -14,6 +14,7 @@ import ResendLineSVG from '@/assets/icons/reset-left-line.svg?react';
import EditLineSVG from '@/assets/icons/quill-pen-line.svg?react'; import EditLineSVG from '@/assets/icons/quill-pen-line.svg?react';
import MailDownloadLineSVG from '@/assets/icons/mail-download-line.svg?react'; import MailDownloadLineSVG from '@/assets/icons/mail-download-line.svg?react';
import MailAddLineSVG from '@/assets/icons/mail-add-line.svg?react'; import MailAddLineSVG from '@/assets/icons/mail-add-line.svg?react';
import TextSVG from '@/assets/icons/text.svg?react';
export const ReplyIcon = (props) => <Icon component={ReplyLineSVG} {...props} />; export const ReplyIcon = (props) => <Icon component={ReplyLineSVG} {...props} />;
@ -29,6 +30,8 @@ export const ResendIcon = (props) => <Icon component={ResendLineSVG} {...props}
export const EditIcon = (props) => <Icon component={EditLineSVG} {...props} />; export const EditIcon = (props) => <Icon component={EditLineSVG} {...props} />;
export const MailDownloadIcon = (props) => <Icon component={MailDownloadLineSVG} {...props} />; export const MailDownloadIcon = (props) => <Icon component={MailDownloadLineSVG} {...props} />;
export const MailAddloadIcon = (props) => <Icon component={MailAddLineSVG} {...props} />; export const MailAddloadIcon = (props) => <Icon component={MailAddLineSVG} {...props} />;
export const TextIcon = (props) => <Icon component={TextSVG} {...props} />;
const WABSvg = () => ( const WABSvg = () => (
<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' width='16' height='16'> <svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' width='16' height='16'>

@ -39,3 +39,5 @@ const __BUILD_DATE__ = `__BUILD_DATE__`;
export const BUILD_VERSION = process.env.NODE_ENV === 'production' ? __BUILD_VERSION__ : process.env.NODE_ENV; export const BUILD_VERSION = process.env.NODE_ENV === 'production' ? __BUILD_VERSION__ : process.env.NODE_ENV;
export const BUILD_DATE = process.env.NODE_ENV === 'production' ? __BUILD_DATE__ : new Date().toLocaleString(); export const BUILD_DATE = process.env.NODE_ENV === 'production' ? __BUILD_DATE__ : new Date().toLocaleString();
export const POPUP_FEATURES = 'left=20,top=20,width=1000,height=800';

@ -54,6 +54,11 @@ export const useEmailDetail = (mai_sn, data) => {
// console.log(data) // console.log(data)
setMailData(data) setMailData(data)
setColiSN(data.info.MAI_COLI_SN) setColiSN(data.info.MAI_COLI_SN)
if (!isEmpty(data.info.MAI_COLI_SN)) {
const orderData = await getEmailOrderAction({ colisn: data.info.MAI_COLI_SN })
setOrderDetail(orderData)
// console.log(orderData)
}
setLoading(false) setLoading(false)
} catch (err) { } catch (err) {
setLoading(false) setLoading(false)
@ -71,29 +76,30 @@ export const useEmailDetail = (mai_sn, data) => {
}, [mai_sn]) }, [mai_sn])
useEffect(() => { // useEffect(() => {
const getOrderDetail = async () => { // console.log(coliSN, '====colisn======')
if (isEmpty(coliSN)) { // const getOrderDetail = async () => {
return false // if (isEmpty(coliSN)) {
} // return false
try { // }
setLoading(true) // try {
const data = await getEmailOrderAction({ colisn: coliSN }) // setLoading(true)
setOrderDetail(data) // const data = await getEmailOrderAction({ colisn: coliSN })
setLoading(false) // setOrderDetail(data)
} catch (err) { // setLoading(false)
setLoading(false) // } catch (err) {
notification.error({ // setLoading(false)
message: "请求失败", // notification.error({
description: err.message || '网络异常', // message: "请求失败",
placement: "top", // description: err.message || '网络异常',
duration: 3, // placement: "top",
}); // duration: 3,
} // });
} // }
// }
getOrderDetail()
}, [coliSN]) // getOrderDetail()
// }, [coliSN])
const postEmailResend = async ({ mai_sn, conversationid: externalid, actionId: actionid, ...body }) => { const postEmailResend = async ({ mai_sn, conversationid: externalid, actionId: actionid, ...body }) => {
if (isEmpty(mai_sn)) { if (isEmpty(mai_sn)) {
@ -108,5 +114,5 @@ export const useEmailDetail = (mai_sn, data) => {
} }
export const EmailBuilder = ({subject, content}) => { export const EmailBuilder = ({subject, content}) => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html lang="en"><head><title>${subject}</title><meta http-equiv="Content-Type" content="text/html charset=UTF-8" /><meta content="width=device-width" name="viewport"><meta charset="UTF-8"><meta content="IE=edge" http-equiv="X-UA-Compatible"><meta content="width=device-width" name="viewport"><meta charset="UTF-8"><meta content="IE=edge" http-equiv="X-UA-Compatible"><meta content="telephone=no,address=no,email=no,date=no,url=no" name="format-detection"><meta content="light" name="color-scheme"><meta content="light" name="supported-color-schemes"><style id="font">body#highlights-email{ font-family: Verdana, sans-serif;} table{ border-collapse: collapse; border-spacing: 0;} </style></head><body id="highlights-email" style="margin: 0 auto; padding: undefined; width: 900px; background-color: #fcfcfc;">${content}</body></html>`; return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html lang="en"><head><title>${subject}</title><meta http-equiv="Content-Type" content="text/html charset=UTF-8" /><meta content="width=device-width" name="viewport"><meta charset="UTF-8"><meta content="IE=edge" http-equiv="X-UA-Compatible"><meta content="width=device-width" name="viewport"><meta charset="UTF-8"><meta content="IE=edge" http-equiv="X-UA-Compatible"><meta content="telephone=no,address=no,email=no,date=no,url=no" name="format-detection"><meta content="light" name="color-scheme"><meta content="light" name="supported-color-schemes"><style id="font">body#highlights-email{ font-family: Verdana, sans-serif;} table{ border-collapse: collapse; border-spacing: 0;} </style></head><body id="highlights-email" style="margin: 0 auto; padding: 0; width: 900px; background-color: #fcfcfc;">${content}</body></html>`;
} }

@ -722,11 +722,11 @@ export const writeIndexDB = (row, table, database) => {
export const deleteIndexDBbyKey = (key, table, database) => { export const deleteIndexDBbyKey = (key, table, database) => {
var open = indexedDB.open(database, INDEXED_DB_VERSION) var open = indexedDB.open(database, INDEXED_DB_VERSION)
open.onupgradeneeded = function () { open.onupgradeneeded = function () {
var db = open.result // var db = open.result
// 数据库是否存在 // // 数据库是否存在
if (!db.objectStoreNames.contains(table)) { // if (!db.objectStoreNames.contains(table)) {
var store = db.createObjectStore(table, { keyPath: 'id', autoIncrement: true }) // var store = db.createObjectStore(table, { keyPath: 'id', autoIncrement: true })
} // }
} }
open.onsuccess = function () { open.onsuccess = function () {
var db = open.result var db = open.result

@ -9,7 +9,7 @@ import { useShallow } from 'zustand/react/shallow';
import MergeConversationTo from './MergeConversationTo'; import MergeConversationTo from './MergeConversationTo';
import BubbleIM from '../Online/Components/BubbleIM'; import BubbleIM from '../Online/Components/BubbleIM';
import BubbleEmail from '../Online/Components/BubbleEmail'; import BubbleEmail from '../Online/Components/BubbleEmail';
import { ERROR_IMG } from '@/config'; import { ERROR_IMG, POPUP_FEATURES } from '@/config';
const BIG_PAGE_SIZE = MESSAGE_PAGE_SIZE * 20; const BIG_PAGE_SIZE = MESSAGE_PAGE_SIZE * 20;
const MessagesList = ({ ...listProps }) => { const MessagesList = ({ ...listProps }) => {
@ -215,7 +215,7 @@ const MessagesList = ({ ...listProps }) => {
return false; return false;
case 'document': case 'document':
window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer'); window.open(msg.data.link || msg.data.uri, msg.data.uri, POPUP_FEATURES);
return false; return false;
default: default:

@ -28,6 +28,7 @@ const EmailContent = ({ id, content: MailContent, className='', ...props }) => {
margin: 0; margin: 0;
padding: 0; padding: 0;
/*overflow-y: hidden;*/ /*overflow-y: hidden;*/
width: 900px;
} }
img { img {
max-width: 90%; max-width: 90%;

@ -1,13 +1,13 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { App, Button, Divider, Avatar, List, Flex, Typography } from 'antd' import { App, Button, Divider, Avatar, List, Flex, Typography } from 'antd'
import { LoadingOutlined, ApiOutlined, FilePdfOutlined, FileOutlined, FileWordOutlined, FileExcelOutlined, FileJpgOutlined, FileImageOutlined, FileTextOutlined, FileGifOutlined, GlobalOutlined, FileZipOutlined } from '@ant-design/icons' import { LoadingOutlined, ApiOutlined, FilePdfOutlined, FileOutlined, FileWordOutlined, FileExcelOutlined, FileJpgOutlined, FileImageOutlined, FileTextOutlined, FileGifOutlined, GlobalOutlined, FileZipOutlined } from '@ant-design/icons'
import { EditIcon, ReplyIcon, ResendIcon, ShareForwardIcon } from '@/components/Icons' import { EditIcon, ReplyAllIcon, ReplyIcon, ResendIcon, ShareForwardIcon } from '@/components/Icons'
import { isEmpty, TagColorStyle } from '@/utils/commons' import { isEmpty, TagColorStyle } from '@/utils/commons'
import EmailEditorPopup from '../Input/EmailEditorPopup' import EmailEditorPopup from '../Input/EmailEditorPopup'
import DnDModal from '@/components/DnDModal' import DnDModal from '@/components/DnDModal'
import useStyleStore from '@/stores/StyleStore' 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, POPUP_FEATURES } from '@/config'
import EmailBindFormModal from './EmailBind' import EmailBindFormModal from './EmailBind'
import EmailContent from './EmailContent' import EmailContent from './EmailContent'
@ -50,7 +50,7 @@ const EmailDetailInline = ({ mailID, emailMsg = {}, disabled = false, ...props }
}, [mailID]) }, [mailID])
const onOpenEditor = (msgOrigin, action='reply') => { const onOpenEditor = (msgOrigin, action='reply') => {
window.open(`/email/${action}/${msgOrigin.email.mai_sn || 0}`, `${action}-${msgOrigin.email.mai_sn || 0}`,'left=20,width=1000') window.open(`/email/${action}/${msgOrigin.email.mai_sn || 0}`, `${action}-${msgOrigin.email.mai_sn || 0}`,POPUP_FEATURES)
console.log('first000', emailMsg) console.log('first000', emailMsg)
console.log('first', msgOrigin) console.log('first', msgOrigin)
if (typeof props.onOpenEditor === 'function') { if (typeof props.onOpenEditor === 'function') {
@ -122,6 +122,11 @@ const EmailDetailInline = ({ mailID, emailMsg = {}, disabled = false, ...props }
回复 回复
</Button>, </Button>,
) )
btns.push(
<Button key={'replyall'} onClick={() => onOpenEditor(emailMsg.msgOrigin, 'replyall')} size='small' type='text' icon={<ReplyAllIcon className='text-indigo-500' />}>
回复全部
</Button>,
)
btns.push( btns.push(
<Button key={'forward'} onClick={() => onOpenEditor(emailMsg.msgOrigin, 'forward')} size='small' type='text' icon={<ShareForwardIcon className='text-primary' />}> <Button key={'forward'} onClick={() => onOpenEditor(emailMsg.msgOrigin, 'forward')} size='small' type='text' icon={<ShareForwardIcon className='text-primary' />}>
转发 转发

@ -14,6 +14,7 @@ import { useOrderStore, } from "@/stores/OrderStore";
import { isEmpty } from '@/utils/commons'; import { isEmpty } from '@/utils/commons';
import useStyleStore from '@/stores/StyleStore'; import useStyleStore from '@/stores/StyleStore';
import EmailListDrawer from './Components/EmailListDrawer'; import EmailListDrawer from './Components/EmailListDrawer';
import { POPUP_FEATURES } from '@/config';
const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => { const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const userId = useAuthStore((state) => state.loginUser.userId); const userId = useAuthStore((state) => state.loginUser.userId);
@ -166,7 +167,7 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
return false; return false;
case 'document': case 'document':
window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer'); window.open(msg.data.link || msg.data.uri, msg.data.link || msg.data.uri, POPUP_FEATURES);
return false; return false;
default: default:

@ -1,11 +1,11 @@
import { useEffect, useState, useRef, useCallback } from 'react' import { useEffect, useState, useRef, useCallback } from 'react'
import { useParams, useNavigate, useLocation } from 'react-router-dom'; import { useParams, useNavigate, useLocation } from 'react-router-dom';
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs } from 'antd' import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs, Radio } from 'antd'
import { UploadOutlined, LoadingOutlined } from '@ant-design/icons' import { UploadOutlined, LoadingOutlined, SaveOutlined, SendOutlined, CheckCircleOutlined } from '@ant-design/icons'
import '@dckj/react-better-modal/dist/index.css' import '@dckj/react-better-modal/dist/index.css'
import DnDModal from '@/components/DnDModal' import DnDModal from '@/components/DnDModal'
import useStyleStore from '@/stores/StyleStore' import useStyleStore from '@/stores/StyleStore'
import useConversationStore from '@/stores/ConversationStore' // import useConversationStore from '@/stores/ConversationStore'
import useAuthStore from '@/stores/AuthStore' import useAuthStore from '@/stores/AuthStore'
import LexicalEditor from '@/components/LexicalEditor' import LexicalEditor from '@/components/LexicalEditor'
@ -17,8 +17,11 @@ import { postSendEmail } from '@/actions/EmailActions'
import { sentMsgTypeMapped } from '@/channel/bubbleMsgUtils' import { sentMsgTypeMapped } from '@/channel/bubbleMsgUtils'
import { EmailBuilder, useEmailDetail, useEmailSignature } from '@/hooks/useEmail' import { EmailBuilder, useEmailDetail, useEmailSignature } from '@/hooks/useEmail'
import useSnippetStore from '@/stores/SnippetStore' import useSnippetStore from '@/stores/SnippetStore'
import { useOrderStore } from '@/stores/OrderStore' // import { useOrderStore } from '@/stores/OrderStore'
import PaymentlinkBtn from '@/views/Conversations/Online/Input/PaymentlinkBtn' import PaymentlinkBtn from '@/views/Conversations/Online/Input/PaymentlinkBtn'
import { TextIcon } from '@/components/Icons';
import GenerateAutoDocDrawer from './Conversations/Online/Components/GenerateAutoDocDrawer';
import { POPUP_FEATURES } from '@/config';
// //
// .application, .exe, .app // .application, .exe, .app
@ -73,7 +76,7 @@ const generateMailContent = (mailData) => `<br><br><p>${mailData.content}</p>`
*/ */
const NewEmail = ({ ...props }) => { const NewEmail = ({ ...props }) => {
const pageParam = useParams(); const pageParam = useParams();
const editorKey = `${pageParam.action}-${pageParam.quoteid}` const editorKey = pageParam.action==='new' ? `new-${Date.now().toString(32)}` : `${pageParam.action}-${pageParam.quoteid}`
const { notification, message } = App.useApp() const { notification, message } = App.useApp()
const [form] = Form.useForm() const [form] = Form.useForm()
@ -85,53 +88,32 @@ const NewEmail = ({ ...props }) => {
const emailListMatMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.mat_sn]: v }), {}) const emailListMatMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.mat_sn]: v }), {})
// console.log('emailListMapped', emailListOption, emailListAddrMapped); // console.log('emailListMapped', emailListOption, emailListAddrMapped);
const emailEdiorProps = useConversationStore((state) => state.emailEdiorProps) // const emailEdiorProps = useConversationStore((state) => state.emailEdiorProps)
const [open, setOpen, closeEditor1, currentEditKey, setCurrentEditKey] = useConversationStore((state) => [ // const [open, setOpen, closeEditor1, currentEditKey, setCurrentEditKey] = useConversationStore((state) => [
state.editorOpen, // state.editorOpen,
state.setEditorOpen, // state.setEditorOpen,
state.closeEditor1, // state.closeEditor1,
state.currentEditKey, // state.currentEditKey,
state.setCurrentEditKey, // state.setCurrentEditKey,
]) // ])
const propsKeysArr = Array.from(emailEdiorProps.keys()) // const propsKeysArr = Array.from(emailEdiorProps.keys())
const propsArr = Array.from(emailEdiorProps.values()) // const propsArr = Array.from(emailEdiorProps.values())
const [activeEdit, setActiveEdit] = useState(emailEdiorProps.get(editorKey) || {}) // const [activeEdit, setActiveEdit] = useState(emailEdiorProps.get(editorKey) || {})
// const { fromEmail, fromUser, fromOrder, oid, toEmail, conversationid, quoteid, initial = {}, mailData: _mailData, action = 'reply', draft = {}, receiverName, ...props } = 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 = pageParam.quoteid // activeEdit.quoteid const mai_sn = pageParam.quoteid // activeEdit.quoteid
const { loading: getLoading, mailData } = useEmailDetail(mai_sn, activeEdit.mailData) const { loading: getLoading, mailData, orderDetail } = useEmailDetail(mai_sn)
const [stickToProps, setStickToProps] = useState({})
const [propsSerialize, setPropsSerialize] = useState('')
const [newFromEmail, setNewFromEmail] = useState('') const [newFromEmail, setNewFromEmail] = useState('')
const [newToEmail, setNewToEmail] = useState('') const [newToEmail, setNewToEmail] = useState('')
const [emailOPI, setEmailOPI] = useState('') const [emailOPI, setEmailOPI] = useState('')
const [emailOrder, setEmailOrder] = useState('') const [emailOrder, setEmailOrder] = useState('')
const [emailOrderSN, setEmailOrderSN] = useState('')
const [emailMat, setEmailMat] = useState('') const [emailMat, setEmailMat] = useState('')
const stateReset = () => { const stateReset = () => {
setStickToProps({})
setStickToCid('')
setEmailOrder('') setEmailOrder('')
setEmailOPI('') setEmailOPI('')
setNewFromEmail('') setNewFromEmail('')
@ -140,60 +122,15 @@ const NewEmail = ({ ...props }) => {
const [contentPrefix, setContentPrefix] = useState('') const [contentPrefix, setContentPrefix] = useState('')
// : ID, // const [stickToCid, setStickToCid] = useState(pageParam.conversationid)
// , 使focus, ID
// , , ID,
const [stickToCid, setStickToCid] = useState(activeEdit.conversationid)
useEffect(() => { useEffect(() => {
console.log('useeffect', mailData.info); console.log('useeffect\n', orderDetail.order_no);
setTimeout(() => { // const propsObj = { ...activeEdit, mai: activeEdit.mailData?.info?.MAI_MAT_SN }
document.title = mailData.info?.MAI_Subject || 'New Email-'
}, 1500);
const propsObj = { ...activeEdit, mai: activeEdit.mailData?.info?.MAI_MAT_SN }
setContentPrefix(activeEdit.oid ? `<p>Dear Mr./Ms. ${activeEdit.receiverName || ''}</p><p>Reference Number: ${activeEdit.oid}</p>` : '') setContentPrefix(orderDetail.order_no ? `<p>Dear Mr./Ms. ${orderDetail.contact?.[0]?.name || ''}</p><p>Reference Number: ${orderDetail.order_no}</p>` : '')
//
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 () => {} return () => {}
}, [open, mailData]) }, [orderDetail.order_no])
const handleSwitchEmail = (labelValue) => { const handleSwitchEmail = (labelValue) => {
const { value } = labelValue const { value } = labelValue
@ -225,8 +162,8 @@ const NewEmail = ({ ...props }) => {
setHtmlContent(htmlContent) setHtmlContent(htmlContent)
setTextContent(textContent) setTextContent(textContent)
form.setFieldValue('content', htmlContent) form.setFieldValue('content', htmlContent)
form.setFieldValue('abstract', getAbstract(textContent)) // form.setFieldValue('abstract', getAbstract(textContent))
debouncedSave({ htmlContent }) debouncedSave({ htmlContent, ...form.getFieldsValue() })
} }
const [initialForm, setInitialForm] = useState({}) const [initialForm, setInitialForm] = useState({})
@ -235,8 +172,49 @@ const NewEmail = ({ ...props }) => {
const [mergeQuote, setMergeQuote] = useState(true) const [mergeQuote, setMergeQuote] = useState(true)
const [quoteContent, setQuoteContent] = useState('') const [quoteContent, setQuoteContent] = useState('')
const setPreFillInProperty = () => { };
useEffect(() => { useEffect(() => {
// console.log('quoteid', quoteid, isEmpty(quoteid), isEmpty(mailData.info)); console.log('useEffect 2---- \nform.setFieldsValue ');
setTimeout(() => {
document.title = mailData.info?.MAI_Subject || 'New Email-'
}, 1500);
//
if (isEmpty(pageParam.quoteid)) {
// setEmailOrder(orderDetail.order_no)
// setEmailOPI(orderDetail.opi_sn)
// 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('')
if (isEmpty(pageParam.quoteid) && pageParam.action !== 'new') { if (isEmpty(pageParam.quoteid) && pageParam.action !== 'new') {
return () => {} return () => {}
@ -254,18 +232,26 @@ const NewEmail = ({ ...props }) => {
setInitialContent(contentPrefix + signatureBody) 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 || ''}` } const forwardValues = { from: newFromEmail, subject: `Fw: ${info.MAI_Subject || ''}` }
if (pageParam.action === 'reply') { if (pageParam.action === 'reply') {
const _formValues = {
to: info?.replyTo || newFromEmail,
cc: info?.MAI_CS || '',
// bcc: quote.bcc || '',
subject: `Re: ${info.MAI_Subject || ''}`,
}
form.setFieldsValue(_formValues)
setInitialForm(_formValues)
} else if (pageParam.action === 'replyall') {
const _formValues = {
to: info?.replyToAll || newFromEmail,
cc: info?.MAI_CS || '',
// bcc: quote.bcc || '',
subject: `Re: ${info.MAI_Subject || ''}`,
}
form.setFieldsValue(_formValues) form.setFieldsValue(_formValues)
setInitialForm(_formValues) setInitialForm(_formValues)
} else if (pageParam.action === 'forward') { } else if (pageParam.action === 'forward') {
setStickToCid('0')
form.setFieldsValue(forwardValues) form.setFieldsValue(forwardValues)
setInitialForm(forwardValues) setInitialForm(forwardValues)
} else if (pageParam.action === 'edit') { } else if (pageParam.action === 'edit') {
@ -288,12 +274,12 @@ const NewEmail = ({ ...props }) => {
} }
return () => {} return () => {}
}, [propsSerialize, mailData.info, signature, newToEmail, newFromEmail]) }, [ mailData.info, signature, newToEmail])
const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false) const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false)
const handlePlainTextOpenChange = ({ target }) => { const handlePlainTextOpenChange = ({ target }) => {
const { checked: newChecked } = target const { value: newChecked } = target
if (!newChecked) { if (newChecked === true) {
setIsRichText(true) setIsRichText(true)
setOpenPlainTextConfirm(false) setOpenPlainTextConfirm(false)
return return
@ -366,7 +352,7 @@ const NewEmail = ({ ...props }) => {
resolve(e.target.result) resolve(e.target.result)
return return
} }
var win = window.open('', file.uid, 'width=1000,height=800,left=20,top=20') var win = window.open('', file.uid, POPUP_FEATURES)
win.document.body.style.margin = '0' win.document.body.style.margin = '0'
if (file.type.startsWith('image/')) { if (file.type.startsWith('image/')) {
win.document.write("<img src='" + e.target.result + '\' style="max-width: 100%;" />') win.document.write("<img src='" + e.target.result + '\' style="max-width: 100%;" />')
@ -407,7 +393,7 @@ const NewEmail = ({ ...props }) => {
* 保存成功, 推一个气泡 * 保存成功, 推一个气泡
* 再从异步通知更新消息发送状态 * 再从异步通知更新消息发送状态
*/ */
const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage) // const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage)
const invokeEmailMessage = (msgObj) => { const invokeEmailMessage = (msgObj) => {
const msgObjMerge = { const msgObjMerge = {
sender: 'me', sender: 'me',
@ -418,13 +404,13 @@ const NewEmail = ({ ...props }) => {
...msgObj, ...msgObj,
// id: `${currentConversation.sn}.${msgObj.id}`, // id: `${currentConversation.sn}.${msgObj.id}`,
// id: `${stickToCid}.${msgObj.id}`, // id: `${stickToCid}.${msgObj.id}`,
conversationid: stickToCid, // conversationid: stickToCid,
msg_source: 'email', msg_source: 'email',
} }
// olog('invoke upload', msgObjMerge) // olog('invoke upload', msgObjMerge)
const contentToRender = sentMsgTypeMapped[msgObjMerge.type].contentToRender(msgObjMerge) const contentToRender = sentMsgTypeMapped[msgObjMerge.type].contentToRender(msgObjMerge)
// console.log(contentToRender, 'contentToRender sendMessage------------------'); // console.log(contentToRender, 'contentToRender sendMessage------------------');
sentOrReceivedNewMessage(contentToRender.conversationid, contentToRender) // sentOrReceivedNewMessage(contentToRender.conversationid, contentToRender)
} }
const [sendLoading, setSendLoading] = useState(false) const [sendLoading, setSendLoading] = useState(false)
@ -440,7 +426,7 @@ const NewEmail = ({ ...props }) => {
body.coli_sn = emailOrder || '' body.coli_sn = emailOrder || ''
// console.log('body', body, '\n', emailOrder); // console.log('body', body, '\n', emailOrder);
const values = await form.validateFields() const values = await form.validateFields()
const preQuoteBody = activeEdit.quoteid ? (quoteContent ? quoteContent : generateQuoteContent(mailData, isRichText)) : '' const preQuoteBody = pageParam.quoteid ? (quoteContent ? quoteContent : generateQuoteContent(mailData, isRichText)) : ''
body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent + preQuoteBody }) : textContent + preQuoteBody body.mailcontent = isRichText ? EmailBuilder({ subject: values.subject, content: htmlContent + preQuoteBody }) : textContent + preQuoteBody
body.cc = values.cc || '' body.cc = values.cc || ''
body.bcc = values.bcc || '' body.bcc = values.bcc || ''
@ -457,15 +443,15 @@ const NewEmail = ({ ...props }) => {
subject: values.subject, subject: values.subject,
content: body.mailcontent, content: body.mailcontent,
}, },
coli_id: stickToProps.oid || (emailOrder ? `{${emailOrder}}` : ''), coli_id: orderDetail.order_no || (emailOrder ? `{${emailOrder}}` : ''),
} }
setSendLoading(true) setSendLoading(true)
body.externalID = stickToCid // body.externalID = stickToCid
body.actionID = `${stickToCid}.${msgObj.id}` // body.actionID = `${stickToCid}.${msgObj.id}`
body.contenttype = isRichText ? 'text/html' : 'text/plain' body.contenttype = isRichText ? 'text/html' : 'text/plain'
try { try {
const bubbleMsg = cloneDeep(msgObj) const bubbleMsg = cloneDeep(msgObj)
bubbleMsg.id = `${stickToCid}.${msgObj.id}` // bubbleMsg.id = `${stickToCid}.${msgObj.id}`
// bubbleMsg.email.mai_sn = ''; // bubbleMsg.email.mai_sn = '';
bubbleMsg.content = undefined bubbleMsg.content = undefined
// invokeEmailMessage(bubbleMsg); // invokeEmailMessage(bubbleMsg);
@ -481,7 +467,7 @@ const NewEmail = ({ ...props }) => {
// setSendLoading(false); // setSendLoading(false);
setOpen(false) // setOpen(false)
} catch (error) { } catch (error) {
notification.error({ notification.error({
message: '邮件保存失败', message: '邮件保存失败',
@ -495,7 +481,7 @@ const NewEmail = ({ ...props }) => {
} }
const [openDrawerSnippet] = useSnippetStore((state) => [state.openDrawer]) const [openDrawerSnippet] = useSnippetStore((state) => [state.openDrawer])
const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer]) // const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer])
const [bakData, setBakData] = useState({}) const [bakData, setBakData] = useState({})
const idleCallbackId = useRef(null) const idleCallbackId = useRef(null)
@ -503,7 +489,7 @@ const NewEmail = ({ ...props }) => {
debounce((data) => { debounce((data) => {
idleCallbackId.current = window.requestIdleCallback(() => { idleCallbackId.current = window.requestIdleCallback(() => {
console.log('Saving data (idle, debounced):', data) console.log('Saving data (idle, debounced):', data)
writeIndexDB({ ...data, key: currentEditKey }, 'draft', 'EmailEditor') writeIndexDB({ ...data, key: editorKey }, 'draft', 'EmailEditor')
}) })
}, 1500), // 1.5s }, 1500), // 1.5s
[], [],
@ -526,6 +512,25 @@ const NewEmail = ({ ...props }) => {
return ( return (
<> <>
<ConfigProvider theme={{ token: { colorPrimary: '#6366f1' } }}> <ConfigProvider theme={{ token: { colorPrimary: '#6366f1' } }}>
<div className='w-full flex gap-4 justify-start items-center text-indigo-600 pb-1 mb-2 border-x-0 border-t-0 border-b border-solid border-neutral-200'>
<Button type='primary' onClick={onHandleSend} loading={sendLoading} icon={<SendOutlined />}>
发送
</Button>
<Select labelInValue options={emailListOption} value={{ key: newFromEmail, value: newFromEmail, label: newFromEmail }} onChange={handleSwitchEmail} labelRender={item => `发件人: ${item.label || '选择'}`} variant={'borderless'} className='[&_.ant-select-selection-item]:font-bold' />
<div className='ml-auto'></div>
<Popconfirm trigger1={['hover', 'click']}
description='切换内容为纯文本格式将丢失信件和签名的格式, 确定使用纯文本?'
onConfirm={confirmPlainText}
open={openPlainTextConfirm}
onCancel={() => setOpenPlainTextConfirm(false)}>
{/* <Checkbox checked={!isRichText} onChange={handlePlainTextOpenChange}>
纯文本
</Checkbox> */}
{/* <Button type='link' size='small' icon={<TextIcon />} className=' ' >纯文本</Button> */}
<Radio.Group options={[{label: '纯文本', value: false}, {label: '富文本', value: true}]} optionType="button" buttonStyle="solid" onChange={handlePlainTextOpenChange} value={isRichText} size='small' />
</Popconfirm>
<Button type='dashed' icon={<SaveOutlined />} size='small' className='' >存草稿</Button>
</div>
<Form <Form
form={form} form={form}
onValuesChange={onEditChange} onValuesChange={onEditChange}
@ -548,32 +553,16 @@ const NewEmail = ({ ...props }) => {
<Flex gap={4}> <Flex gap={4}>
{!showCc && ( {!showCc && (
<Button type='text' onClick={handleShowCc}> <Button type='text' onClick={handleShowCc}>
Cc 抄送
</Button> </Button>
)} )}
{!showBcc && ( {!showBcc && (
<Button type='text' hidden={showBcc} onClick={handleShowBcc}> <Button type='text' hidden={showBcc} onClick={handleShowBcc}>
Bcc 密送
</Button> </Button>
)} )}
</Flex> </Flex>
</Space.Compact> </Space.Compact>
{/* <Input
addonAfter={
<Flex gap={4}>
{!showCc && (
<Button type='text' onClick={handleShowCc}>
Cc
</Button>
)}
{!showBcc && (
<Button type='text' hidden={showBcc} onClick={handleShowBcc}>
Bcc
</Button>
)}
</Flex>
}
/> */}
</Form.Item> </Form.Item>
<Form.Item label='抄&nbsp;&nbsp;&nbsp;&nbsp;送' name={'cc'} hidden={!showCc} className='w-full pt-1'> <Form.Item label='抄&nbsp;&nbsp;&nbsp;&nbsp;送' name={'cc'} hidden={!showCc} className='w-full pt-1'>
<Input /> <Input />
@ -615,10 +604,10 @@ const NewEmail = ({ ...props }) => {
</Form.Item> </Form.Item>
</Form> </Form>
<LexicalEditor {...{ isRichText }} onChange={handleEditorChange} defaultValue={initialContent} /> <LexicalEditor {...{ isRichText }} onChange={handleEditorChange} defaultValue={initialContent} />
{activeEdit.quoteid && !showQuoteContent && ( {pageParam.quoteid && !showQuoteContent && (
<div className='flex justify-start items-center ml-2'> <div className='flex justify-start items-center ml-2'>
<Button className='flex gap-2 ' type='link' onClick={() => setShowQuoteContent(!showQuoteContent)}> <Button className='flex gap-2 ' type='link' onClick={() => setShowQuoteContent(!showQuoteContent)}>
显示引用内容 显示引用内容 {/*(不可更改)*/}
</Button> </Button>
{/* <Button className='flex gap-2 ' type='link' danger onClick={() => {setMergeQuote(false);setShowQuoteContent(false)}}> {/* <Button className='flex gap-2 ' type='link' danger onClick={() => {setMergeQuote(false);setShowQuoteContent(false)}}>
删除引用内容 删除引用内容
@ -627,8 +616,8 @@ const NewEmail = ({ ...props }) => {
)} )}
{showQuoteContent && ( {showQuoteContent && (
<blockquote <blockquote
contentEditable // contentEditable
className='border-0 outline-none' className='border-0 outline-none cursor-text'
onBlur={(e) => setQuoteContent(`<blockquote>${e.target.innerHTML}</blockquote>`)} onBlur={(e) => setQuoteContent(`<blockquote>${e.target.innerHTML}</blockquote>`)}
dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></blockquote> dangerouslySetInnerHTML={{ __html: generateQuoteContent(mailData) }}></blockquote>
)} )}

Loading…
Cancel
Save