feat: 保存草稿: 附件处理

dev/ckeditor
Lei OT 4 months ago
parent aeb0672002
commit 05a22161cd

@ -167,7 +167,7 @@ const todoTypes = {
*/ */
export const getEmailDirAction = async (params = { opi_sn: '' }) => { export const getEmailDirAction = async (params = { opi_sn: '' }) => {
const defaultParams = { opi_sn: 0, year: dayjs().year(), by_start_date: -1, by_success: -1, important: -1, if_want_book: -1, if_thinking: -1 } const defaultParams = { opi_sn: 0, year: dayjs().year(), by_start_date: -1, by_success: -1, important: -1, if_want_book: -1, if_thinking: -1 }
const { errcode, result } = await fetchJSON(`${API_HOST}/v3/email_dir`, { ...defaultParams, ...params }) const { errcode, result } = await fetchJSON(`${API_HOST}/email_dir`, { ...defaultParams, ...params })
const mailboxSort = result //.sort(sortBy('MDR_Order')); const mailboxSort = result //.sort(sortBy('MDR_Order'));
let tree = buildTree(mailboxSort, { key: 'VKey', parent: 'VParent', name: 'VName', iconIndex: 'ImageIndex', rootKeys: [1], ignoreKeys: [-227001, -227002] }) let tree = buildTree(mailboxSort, { key: 'VKey', parent: 'VParent', name: 'VName', iconIndex: 'ImageIndex', rootKeys: [1], ignoreKeys: [-227001, -227002] })
tree = tree.filter((ele) => ele.key !== 1) tree = tree.filter((ele) => ele.key !== 1)
@ -309,7 +309,7 @@ export const queryEmailListAction = async ({ opi_sn = '', pagesize = 10, last_id
} }
_params.mai_senddate1 = dayjs(_params.mai_senddate1).format(DATE_FORMAT) _params.mai_senddate1 = dayjs(_params.mai_senddate1).format(DATE_FORMAT)
const cacheKey = isEmpty(_params.coli_sn) ? `dir-${node.vkey}` : `order-${node.vkey}`; const cacheKey = isEmpty(_params.coli_sn) ? `dir-${node.vkey}` : `order-${node.vkey}`;
const { errcode, result } = await fetchJSON(`${API_HOST}/v3/mail_list`, _params) const { errcode, result } = await fetchJSON(`${API_HOST}/mail_list`, _params)
const ret = errcode === 0 ? result : [] const ret = errcode === 0 ? result : []
if (!isEmpty(ret)) { if (!isEmpty(ret)) {
const listids = [...new Set(ret.map(ele => ele.MAI_SN))]; const listids = [...new Set(ret.map(ele => ele.MAI_SN))];
@ -337,7 +337,7 @@ export const updateEmailAction = async (params = { mai_sn_list: [], set: {} }) =
* @param {number} [params.remind_index] - Index of the reminder. * @param {number} [params.remind_index] - Index of the reminder.
*/ */
export const getEmailTemplateAction = async (params = { coli_sn: 0, lgc: 1, opi_sn: 0, remind_type: '', remind_index: 0 }) => { export const getEmailTemplateAction = async (params = { coli_sn: 0, lgc: 1, opi_sn: 0, remind_type: '', remind_index: 0 }) => {
const { errcode, result } = await fetchJSON(`${API_HOST}/v3/reminder_letter`, params) const { errcode, result } = await fetchJSON(`${API_HOST}/reminder_letter`, params)
const { html, bodyContent, bodyText } = parseHTMLString(result?.MailContent, true) ; const { html, bodyContent, bodyText } = parseHTMLString(result?.MailContent, true) ;
return errcode === 0 ? {...result, bodyContent }: {} return errcode === 0 ? {...result, bodyContent }: {}
} }
@ -348,24 +348,25 @@ export const getEmailTemplateAction = async (params = { coli_sn: 0, lgc: 1, opi_
* @param {boolean} [isDraft=false] - Whether the email is a draft. * @param {boolean} [isDraft=false] - Whether the email is a draft.
*/ */
export const saveEmailDraftOrSendAction = async (body, isDraft = false) => { export const saveEmailDraftOrSendAction = async (body, isDraft = false) => {
const url = isDraft !== false ? `${API_HOST}/v3/email_draft_save` : `${EMAIL_HOST}/sendmail`; const url = isDraft !== false ? `${API_HOST}/email_draft_save` : `${EMAIL_HOST}/sendmail`;
const { attaList=[], atta, content, ...bodyData } = body; const { attaList=[], atta, content, ...bodyData } = body;
bodyData.ordertype = 227001; bodyData.ordertype = 227001;
const formData = new FormData(); const formData = new FormData();
Object.keys(bodyData).forEach(function (key) { Object.keys(bodyData).forEach(function (key) {
formData.append(key, bodyData[key]); formData.append(key, bodyData[key] || '');
}); });
attaList.forEach(function (item) { // 附件只传新增的
attaList.filter(ele => !ele.fullPath).forEach(function (item) {
formData.append('attachment', item); formData.append('attachment', item);
}); });
const { result } = await postForm(url, formData); const { errcode, result } = await postForm(url, formData);
return result; return errcode === 0 ? (result || {}) : {}
}; };
/** /**
* 删除邮件草稿 * 删除邮件附件
*/ */
export const deleteEmailDraftAction = async (id) => { export const deleteEmailAttachmentAction = async (ati_sn_list) => {
const { errcode, result } = await postJSON(`${EMAIL_HOST}/email-draft/delete`, { id }) const { errcode, result } = await postJSON(`${API_HOST}/mail_attachment_delete`, { ati_sn_list })
return errcode === 0 ? {} : result return errcode === 0 ? result : {}
}; };

@ -6,7 +6,7 @@
// export const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_144'; // prod: Slave // export const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_144'; // prod: Slave
// debug: // debug:
export const API_HOST = 'http://202.103.68.144:8889/v2'; export const API_HOST = 'http://202.103.68.144:8889/v3';
// export const WS_URL = 'ws://202.103.68.144:8888'; // export const WS_URL = 'ws://202.103.68.144:8888';
// export const EMAIL_HOST = 'http://202.103.68.231:888/service-mail'; // export const EMAIL_HOST = 'http://202.103.68.231:888/service-mail';
// export const WAI_HOST = 'http://47.83.248.120/api/v1'; // 香港服务器 // export const WAI_HOST = 'http://47.83.248.120/api/v1'; // 香港服务器

@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback } from 'react'
import { isEmpty, objectMapper, olog, readIndexDB, } from '@/utils/commons' import { isEmpty, objectMapper, olog, readIndexDB, } from '@/utils/commons'
import { getEmailDetailAction, postResendEmailAction, getSalesSignatureAction, getEmailOrderAction, queryEmailListAction, getEmailTemplateAction } from '@/actions/EmailActions' import { getEmailDetailAction, postResendEmailAction, getSalesSignatureAction, getEmailOrderAction, queryEmailListAction, getEmailTemplateAction, saveEmailDraftOrSendAction } from '@/actions/EmailActions'
import { App } from 'antd' import { App } from 'antd'
import useConversationStore from '@/stores/ConversationStore'; import useConversationStore from '@/stores/ConversationStore';
import { msgStatusRenderMapped } from '@/channel/bubbleMsgUtils'; import { msgStatusRenderMapped } from '@/channel/bubbleMsgUtils';
@ -44,79 +44,96 @@ export const useEmailDetail = (mai_sn, data={}, oid=0) => {
const {notification} = App.useApp() const {notification} = App.useApp()
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [mailData, setMailData] = useState({ loading, info: { MAI_COLI_SN: 0 }, content: '', attachments: [], AttachList: [] }) const [mailData, setMailData] = useState({ loading, info: { MAI_COLI_SN: 0 }, content: '', attachments: [], AttachList: [] })
const [maiSN, setMaiSN] = useState(mai_sn);
const [coliSN, setColiSN] = useState(oid); const [coliSN, setColiSN] = useState(oid);
const [orderDetail, setOrderDetail] = useState({}); const [orderDetail, setOrderDetail] = useState({});
const [updateMessageItem] = useConversationStore(state => [state.updateMessageItem]); const [refreshTrigger, setRefreshTrigger] = useState(0);
const refresh = useCallback(() => {
setRefreshTrigger(prev => prev + 1);
}, []);
useEffect(() => { // const [updateMessageItem] = useConversationStore(state => [state.updateMessageItem]);
const getEmailDetail = async () => {
if (isEmpty(Number(mai_sn))) { const getEmailDetail = useCallback(async () => {
return false if (isEmpty(Number(maiSN)) && isEmpty(Number(mai_sn))) {
} return false
try {
setLoading(true)
const data = await getEmailDetailAction({ mai_sn })
// console.log(data)
setMailData(data)
setColiSN(oid===false ? 0 : 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)
} catch (err) {
setLoading(false)
notification.error({
message: "请求失败",
description: err.message || '网络异常',
placement: "top",
duration: 3,
});
}
} }
if (isEmpty(data)) getEmailDetail() if (!isEmpty(data)) {
else setMailData(data) setMailData(data)
}, [mai_sn]) return false
}
try {
setLoading(true)
const data = await getEmailDetailAction({ mai_sn: maiSN || mai_sn })
// console.log(data)
setMailData(data)
setColiSN(oid === false ? 0 : data.info.MAI_COLI_SN)
setLoading(false)
} catch (err) {
setLoading(false)
notification.error({
message: '请求失败',
description: err.message || '网络异常',
placement: 'top',
duration: 3,
})
}
}, [mai_sn, maiSN, refreshTrigger])
useEffect(() => {
console.log(coliSN, '====colisn======') const getOrderDetail = useCallback(async () => {
const getOrderDetail = async () => { // console.log(coliSN, '====colisn======')
if (isEmpty(Number(coliSN))) { if (isEmpty(Number(coliSN))) {
return false return false
} }
try { try {
setLoading(true) setLoading(true)
const data = await getEmailOrderAction({ colisn: coliSN }) const data = await getEmailOrderAction({ colisn: coliSN })
setOrderDetail(data) setOrderDetail(data)
setLoading(false) setLoading(false)
} catch (err) { } catch (err) {
setLoading(false) setLoading(false)
notification.error({ notification.error({
message: "请求失败", message: '请求失败',
description: err.message || '网络异常', description: err.message || '网络异常',
placement: "top", placement: 'top',
duration: 3, duration: 3,
}); })
}
} }
}, [coliSN])
useEffect(() => {
getEmailDetail();
}, [getEmailDetail])
useEffect(() => {
getOrderDetail() getOrderDetail()
}, [coliSN]) }, [getOrderDetail])
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(Number(mai_sn))) {
return false return false
} }
await postResendEmailAction({ mai_sn, externalid, actionid, token: 0 }) await postResendEmailAction({ mai_sn, externalid, actionid, token: 0 })
// 重发没有状态返回值, 此处前端处理为'待发送', todo: 刷新页面后消息仍为上一个状态'失败' // 重发没有状态返回值, 此处前端处理为'待发送', todo: 刷新页面后消息仍为上一个状态'失败'
updateMessageItem({ conversationid: externalid, actionid, id: actionid, status: msgStatusRenderMapped['accepted'] }) // updateMessageItem({ conversationid: externalid, actionid, id: actionid, status: msgStatusRenderMapped['accepted'] })
} }
return { loading, mailData, orderDetail, postEmailResend } const postEmailSaveOrSend = async (body, isDraft) => {
try {
const { id: savedID } = await saveEmailDraftOrSendAction(body, isDraft)
setMaiSN(savedID)
refresh()
return savedID
} catch (error) {
console.error(error);
}
};
return { loading, mailData, orderDetail, postEmailResend, postEmailSaveOrSend }
} }
export const EmailBuilder = ({subject, content}) => { export const EmailBuilder = ({subject, content}) => {

@ -68,9 +68,7 @@ const EmailDetailInline = ({ mailID, emailMsg = {}, disabled = false, variant, s
}, [mailID]) }, [mailID])
const onOpenEditor = (msgOrigin, action='reply') => { const onOpenEditor = (msgOrigin, action='reply') => {
window.open(`/email/${action}/${mailID || 0}`, `${action}-${mailID || 0}`,POPUP_FEATURES) openPopup(`/email/${action}/${mailID || 0}`, `${action}-${mailID || 0}`)
console.log('first000', emailMsg)
console.log('first', msgOrigin)
if (typeof props.onOpenEditor === 'function') { if (typeof props.onOpenEditor === 'function') {
props.onOpenEditor(msgOrigin, action); props.onOpenEditor(msgOrigin, action);
} else { } else {
@ -256,7 +254,7 @@ const EmailDetailInline = ({ mailID, emailMsg = {}, disabled = false, variant, s
<List.Item> <List.Item>
<FileTypeIcon fileName={atta.ATI_Name} /> <FileTypeIcon fileName={atta.ATI_Name} />
<Typography.Text ellipsis={{ tooltip: { title: atta.ATI_Name, placement: 'left' } }} className='text-inherit'> <Typography.Text ellipsis={{ tooltip: { title: atta.ATI_Name, placement: 'left' } }} className='text-inherit'>
<span key={atta.ATI_SN} onClick={() => openPopup(`${EMAIL_ATTA_HOST}${atta.ATI_ServerFile}`, atta.ATI_SN, POPUP_FEATURES)} size='small' className='text-blue-500 cursor-pointer'> <span key={atta.ATI_SN} onClick={() => openPopup(`${EMAIL_ATTA_HOST}${atta.ATI_ServerFile}`, atta.ATI_SN)} size='small' className='text-blue-500 cursor-pointer'>
{atta.ATI_Name} {atta.ATI_Name}
</span> </span>
</Typography.Text> </Typography.Text>

@ -10,16 +10,16 @@ import useAuthStore from '@/stores/AuthStore'
import LexicalEditor from '@/components/LexicalEditor' import LexicalEditor from '@/components/LexicalEditor'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { cloneDeep, debounce, isEmpty, writeIndexDB, readIndexDB, deleteIndexDBbyKey, olog } from '@/utils/commons' import { cloneDeep, debounce, isEmpty, writeIndexDB, readIndexDB, deleteIndexDBbyKey, olog, omitEmpty } from '@/utils/commons'
import '@/views/Conversations/Online/Input/EmailEditor.css' import '@/views/Conversations/Online/Input/EmailEditor.css'
import { parseHTMLString, postSendEmail, saveEmailDraftOrSendAction } from '@/actions/EmailActions' import { deleteEmailAttachmentAction, parseHTMLString, postSendEmail, saveEmailDraftOrSendAction } from '@/actions/EmailActions'
import { sentMsgTypeMapped } from '@/channel/bubbleMsgUtils' import { sentMsgTypeMapped } from '@/channel/bubbleMsgUtils'
import { EmailBuilder, useEmailDetail, useEmailSignature, useEmailTemplate } from '@/hooks/useEmail' import { EmailBuilder, openPopup, useEmailDetail, useEmailSignature, useEmailTemplate } 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 { TextIcon } from '@/components/Icons';
import { POPUP_FEATURES } from '@/config'; import { EMAIL_ATTA_HOST, POPUP_FEATURES } from '@/config';
// //
// .application, .exe, .app // .application, .exe, .app
@ -92,23 +92,16 @@ const NewEmail = () => {
// console.log('emailListMapped', emailListOption, emailListAddrMapped); // console.log('emailListMapped', emailListOption, emailListAddrMapped);
const mai_sn = pageParam.quoteid // activeEdit.quoteid const mai_sn = pageParam.quoteid // activeEdit.quoteid
const { loading: quoteLoading, mailData, orderDetail } = useEmailDetail(mai_sn, null, pageParam.oid) const { loading: quoteLoading, mailData, orderDetail, postEmailSaveOrSend } = useEmailDetail(mai_sn, null, pageParam.oid)
const { loading: loadingTamplate, templateContent } = useEmailTemplate(templateKey, {coli_sn: pageParam.oid, opi_sn: orderDetail.opi_sn || mailData.info?.MAI_OPI_SN || 0, lgc: 1}); const { loading: loadingTamplate, templateContent } = useEmailTemplate(templateKey, {coli_sn: pageParam.oid, opi_sn: orderDetail.opi_sn || mailData.info?.MAI_OPI_SN || 0, lgc: 1});
const { signature } = useEmailSignature(orderDetail.opi_sn || mailData.info?.MAI_OPI_SN || 0) const { signature } = useEmailSignature(orderDetail.opi_sn || mailData.info?.MAI_OPI_SN || 0)
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 [emailOrderSN, setEmailOrderSN] = useState('')
const [emailMat, setEmailMat] = useState('') // const [emailMat, setEmailMat] = useState('')
const stateReset = () => {
setEmailOrder('')
setEmailOPI('')
setNewFromEmail('')
setNewToEmail('')
}
const [contentPrefix, setContentPrefix] = useState('') const [contentPrefix, setContentPrefix] = useState('')
const [localDraft, setLocalDraft] = useState(); const [localDraft, setLocalDraft] = useState();
@ -165,13 +158,16 @@ const NewEmail = () => {
// - // -
// - // -
useEffect(() => { useEffect(() => {
console.log('useEffect 1---- \nform.setFieldsValue '); // console.log('useEffect 1---- \nform.setFieldsValue ');
if (isEmpty(mailData.content) && isEmpty(orderDetail.order_no)) { if (isEmpty(mailData.content) && isEmpty(orderDetail.order_no)) {
return () => {} return () => {}
} }
const docTitle = mailData.info?.MAI_Subject || 'New Email-'; const docTitle = mailData.info?.MAI_Subject || 'New Email-';
document.title = docTitle document.title = docTitle
const { order_no } = orderDetail
setContentPrefix(order_no ? `<p>Dear Mr./Ms. ${orderDetail.contact?.[0]?.name || ''}</p><p>Reference Number: ${order_no}</p>` : '')
const { info } = mailData const { info } = mailData
const { ...templateFormValues } = templateContent; const { ...templateFormValues } = templateContent;
@ -183,13 +179,12 @@ const NewEmail = () => {
const _findMatOldE = emailListMatMapped?.[info.MAI_MAT_SN] const _findMatOldE = emailListMatMapped?.[info.MAI_MAT_SN]
const quotedMailSender = _findMatOldE?.email || '' const quotedMailSender = _findMatOldE?.email || ''
const sender = quotedMailSender || orderSender const sender = quotedMailSender || orderSender
const quotedMailSenderObj = sender; // { key: sender, label: sender, value: sender } const quotedMailSenderObj = sender; // { key: sender, label: sender, value: sender }
const defaultMAT = emailListAddrMapped?.[sender]?.mat_sn || '' const defaultMAT = emailListAddrMapped?.[sender]?.mat_sn || ''
const _form2 = { const _form2 = {
coli_sn: pageParam.oid || info?.coli_sn || '', coli_sn: Number(pageParam.oid) || info?.MAI_COLI_SN || '',
mat_sn: info?.MAI_MAT_SN || defaultMAT, mat_sn: info?.MAI_MAT_SN || defaultMAT,
opi_sn: info?.MAI_OPI_SN || orderDetail.opi_sn || '', opi_sn: info?.MAI_OPI_SN || orderDetail.opi_sn || '',
} }
@ -248,21 +243,24 @@ const NewEmail = () => {
..._form2 ..._form2
} }
readyToInitialContent = generateMailContent(mailData) readyToInitialContent = generateMailContent(mailData)
setFileList(mailData.attachments.map(ele => ({ uid: ele.ATI_SN, name: ele.ATI_Name, url: ele.ATI_ServerFile, fullPath: `${EMAIL_ATTA_HOST}${ele.ATI_ServerFile}` })))
break break
case 'new': case 'new':
_formValues = { _formValues = {
...templateFormValues, ...templateFormValues,
from: quotedMailSenderObj, from: quotedMailSenderObj,
to: orderReceiver, to: orderReceiver || info?.MAI_To || '',
..._form2 subject: `${info.MAI_Subject || ''}`,
..._form2,
} }
readyToInitialContent = generateMailContent({ content: templateContent.bodycontent }) readyToInitialContent = generateMailContent({ content: templateContent.bodycontent || '' })
setFileList(mailData.attachments.map(ele => ({ uid: ele.ATI_SN, name: ele.ATI_Name, url: ele.ATI_ServerFile, fullPath: `${EMAIL_ATTA_HOST}${ele.ATI_ServerFile}` })))
break break
default: default:
break break
} }
olog('222', _formValues, pageParam.action) // olog('222', _formValues, pageParam.action)
form.setFieldsValue(_formValues) // todo: from form.setFieldsValue(_formValues) // todo: from
setInitialContent(readyToInitialContent); setInitialContent(readyToInitialContent);
@ -306,27 +304,13 @@ const NewEmail = () => {
return () => {} return () => {}
}, [loadingTamplate]) }, [loadingTamplate])
const handleSwitchEmail = (value) => {
// const { value } = labelValue
useEffect(() => { // setNewFromEmail(value)
console.log('useeffect\n', orderDetail.order_no);
setContentPrefix(orderDetail.order_no ? `<p>Dear Mr./Ms. ${orderDetail.contact?.[0]?.name || ''}</p><p>Reference Number: ${orderDetail.order_no}</p>` : '')
// form.setFieldsValue({ to: orderDetail.contact?.[0]?.email || '' })
setNewToEmail(orderDetail.contact?.[0]?.email || '')
return () => {}
}, [orderDetail.order_no, editorKey])
const handleSwitchEmail = (labelValue) => {
const { value } = labelValue
setNewFromEmail(value)
const _findMat = emailListAddrMapped?.[value] const _findMat = emailListAddrMapped?.[value]
setEmailMat(_findMat?.mat_sn) form.setFieldsValue({ mat_sn: _findMat?.mat_sn, opi_sn: _findMat?.opi_sn })
setEmailOPI(_findMat?.opi_sn)
} }
const [isRichText, setIsRichText] = useState(mobile === false) const [isRichText, setIsRichText] = useState(mobile === false)
// const [isRichText, setIsRichText] = useState(false); // // const [isRichText, setIsRichText] = useState(false); //
const [htmlContent, setHtmlContent] = useState('') const [htmlContent, setHtmlContent] = useState('')
@ -348,125 +332,17 @@ const NewEmail = () => {
setTextContent(textContent) setTextContent(textContent)
form.setFieldValue('content', htmlContent) form.setFieldValue('content', htmlContent)
const { bodyText: abstract } = parseHTMLString(htmlContent, true); const { bodyText: abstract } = parseHTMLString(htmlContent, true);
const body = {
// from: newFromEmail,
// attaList: fileList,
// opi_sn: emailOPI,
// mat_sn: emailMat,
// coli_sn: emailOrder || '',
}
// form.setFieldValue('abstract', getAbstract(textContent)) // form.setFieldValue('abstract', getAbstract(textContent))
debouncedSave({ ...form.getFieldsValue(), ...body, htmlContent, abstract, }) const formValues = omitEmpty(form.getFieldsValue());
if (!isEmpty(formValues)) {
debouncedSave({ ...form.getFieldsValue(), htmlContent, abstract, })
}
} }
const [initialContent, setInitialContent] = useState('') const [initialContent, setInitialContent] = useState('')
const [showQuoteContent, setShowQuoteContent] = useState(false) const [showQuoteContent, setShowQuoteContent] = useState(false)
// const [mergeQuote, setMergeQuote] = useState(true)
const [quoteContent, setQuoteContent] = useState('') const [quoteContent, setQuoteContent] = useState('')
// const setPreFillInProperty = () => { };
// useEffect(() => {
// console.log('useEffect 2---- \nform.setFieldsValue ');
// const docTitle = localDraft?.subject || mailData.info?.MAI_Subject || 'New Email-';
// setTimeout(() => {
// document.title = docTitle
// }, 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') {
// 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) && pageParam.action !== 'edit') {
// setInitialContent(contentPrefix + signatureBody)
// }
// const forwardValues = { from: newFromEmail, subject: `Fw: ${info.MAI_Subject || ''}` }
// 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)
// } 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)
// } else if (pageParam.action === 'forward') {
// form.setFieldsValue(forwardValues)
// } else if (pageParam.action === 'edit') {
// const thisFormValues = {
// to: info?.MAI_To || '',
// cc: info?.MAI_CS || '',
// subject: info?.MAI_Subject || '',
// id: pageParam.quoteid,
// }
// form.setFieldsValue(thisFormValues)
// const thisBody = generateMailContent(mailData)
// // console.log('thisBody', thisBody);
// setInitialContent(thisBody)
// } else if (pageParam.action === 'new') {
// // todo:
// const newEmail = { to: newToEmail, subject: '' }
// // form.setFieldsValue(newEmail)
// // setInitialContent((contentPrefix || '') + signatureBody)
// }
// return () => {}
// }, [ mailData.info, signature, newToEmail, contentPrefix])
const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false) const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false)
const handlePlainTextOpenChange = ({ target }) => { const handlePlainTextOpenChange = ({ target }) => {
const { value: newChecked } = target const { value: newChecked } = target
@ -486,18 +362,18 @@ const NewEmail = () => {
// 1. ~~ // 1. ~~
// 2. // 2.
const [fileList, setFileList] = useState([]) const [fileList, setFileList] = useState([])
const handleChange = (info) => { // const handleChange = (info) => {
let newFileList = [...info.fileList] // let newFileList = [...info.fileList]
// 2. Read from response and show file link // // 2. Read from response and show file link
newFileList = newFileList.map((file) => { // newFileList = newFileList.map((file) => {
if (file.response) { // if (file.response) {
// Component will show file.url as link // // Component will show file.url as link
file.url = file.response.url // file.url = file.response.url
} // }
return file // return file
}) // })
setFileList(newFileList) // setFileList(newFileList)
} // }
const normFile = (e) => { const normFile = (e) => {
// console.log('Upload event:', e); // console.log('Upload event:', e);
if (Array.isArray(e)) { if (Array.isArray(e)) {
@ -522,7 +398,24 @@ const NewEmail = () => {
setFileList((prev) => [...prev, file]) setFileList((prev) => [...prev, file])
return false // , return false // ,
}, },
onRemove: (file) => { onRemove: async (file) => {
console.log('onRomove', file)
if (file.fullPath) {
try {
const x = await deleteEmailAttachmentAction([file.uid]);
message.success(`已删除 ${file.name}`, 2)
} catch (error) {
console.error(error)
notification.error({
key: editorKey,
message: '删除失败',
description: error.message,
placement: 'top',
duration: 3,
})
return false;
}
}
const index = fileList.indexOf(file) const index = fileList.indexOf(file)
const newFileList = fileList.slice() const newFileList = fileList.slice()
newFileList.splice(index, 1) newFileList.splice(index, 1)
@ -569,7 +462,10 @@ const NewEmail = () => {
// win.document.write("<iframe src='" + dataURL + "' width='100%' height='100%' style=\"border:none\"></iframe>"); // win.document.write("<iframe src='" + dataURL + "' width='100%' height='100%' style=\"border:none\"></iframe>");
resolve(reader.result) resolve(reader.result)
} }
if (file.type.startsWith('text/') || file.type === 'application/html' || file.type === 'application/xhtml+xml') { if (file.fullPath) {
openPopup(file.fullPath, file.uid)
}
else if (file.type.startsWith('text/') || file.type === 'application/html' || file.type === 'application/xhtml+xml') {
reader.readAsText(file) reader.readAsText(file)
} else { } else {
reader.readAsDataURL(file) reader.readAsDataURL(file)
@ -580,102 +476,65 @@ const NewEmail = () => {
}, },
} }
/**
* 保存成功, 推一个气泡
* 再从异步通知更新消息发送状态
*/
// const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage)
const invokeEmailMessage = (msgObj) => {
const msgObjMerge = {
sender: 'me',
senderName: 'me',
// to: currentConversation.whatsapp_phone_number,
date: new Date(),
status: 'waiting', // accepted
...msgObj,
// id: `${currentConversation.sn}.${msgObj.id}`,
// id: `${stickToCid}.${msgObj.id}`,
// conversationid: stickToCid,
msg_source: 'email',
}
// 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 [sendLoading, setSendLoading] = useState(false)
const onHandleSaveOrSend = async (isDraft = false) => { const onHandleSaveOrSend = async (isDraft = false) => {
// console.log('onSend callback', '\nisRichText', isRichText); // console.log('onSend callback', '\nisRichText', isRichText);
console.log(form.getFieldsValue()); // console.log(form.getFieldsValue());
const body = structuredClone(form.getFieldsValue()) const body = structuredClone(form.getFieldsValue())
// body.mailtype = ''; // todo: body.attaList = fileList;
// body.id = ''; // todo: 稿 // console.log('body', body, '\n', fileList);
// body.from = newFromEmail // todo:
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() const values = await form.validateFields()
const preQuoteBody = pageParam.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 || ''
const msgObj = { body.bcc = values.mailtype || ''
type: 'email', setSendLoading(!isDraft)
id: uuid(), notification.open({
from: body.from, key: editorKey,
to: values.to, placement: 'top',
cc: values.cc || '', // message: '',
bcc: values.bcc || '', description: '正在保存...',
subject: values.subject, duration: 0,
content: body.mailcontent, icon: <LoadingOutlined className='text-primary' />,
email: { closable: false,
subject: values.subject, })
content: body.mailcontent,
},
coli_id: orderDetail.order_no || (emailOrder ? `{${emailOrder}}` : ''),
}
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)
// bubbleMsg.id = `${stickToCid}.${msgObj.id}`
// bubbleMsg.email.mai_sn = '';
bubbleMsg.content = undefined
// invokeEmailMessage(bubbleMsg);
// console.log('postSendEmail', body, '\n', msgObj); // console.log('postSendEmail', body, '\n', msgObj);
// return; // return;
// todo: 稿, ; ID // todo: 稿, ; ID
const result = await saveEmailDraftOrSendAction(body, isDraft) const mailSavedId = await postEmailSaveOrSend(body, isDraft)
const mailSavedId = result.id || '' form.setFieldsValue({
bubbleMsg.email.mai_sn = mailSavedId mai_sn: mailSavedId,
// console.log('invokeEmailMessage', bubbleMsg); id: mailSavedId,
})
// invokeEmailMessage(bubbleMsg) // bubbleMsg.email.mai_sn = mailSavedId
// setSendLoading(false); // setSendLoading(false);
notification.success({ if (!isDraft) {
key: editorKey, notification.success({
message: '成功', key: editorKey,
// description: error.message, message: '成功',
placement: 'top', description: isDraft ? '' : '窗口将自动关闭...',
duration: 2, placement: 'top',
showProgress: true, duration: 2,
pauseOnHover: true, showProgress: true,
onClose: () => { pauseOnHover: true,
deleteIndexDBbyKey(editorKey, 'draft', 'mailbox'); onClose: () => {
window.close(); deleteIndexDBbyKey(editorKey, 'draft', 'mailbox');
}, isDraft ? false : window.close();
}) },
})
} else { notification.destroy(editorKey) }
// setOpen(false) // setOpen(false)
} catch (error) { } catch (error) {
console.error(error)
notification.error({ notification.error({
key: editorKey, key: editorKey,
message: '邮件保存失败', message: '邮件保存失败',
@ -689,9 +548,7 @@ const NewEmail = () => {
} }
const [openDrawerSnippet] = useSnippetStore((state) => [state.openDrawer]) const [openDrawerSnippet] = useSnippetStore((state) => [state.openDrawer])
// const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer])
const [bakData, setBakData] = useState({})
const idleCallbackId = useRef(null) const idleCallbackId = useRef(null)
const debouncedSave = useCallback( const debouncedSave = useCallback(
debounce((data) => { debounce((data) => {
@ -702,13 +559,6 @@ const NewEmail = () => {
}, 1500), // 1.5s }, 1500), // 1.5s
[], [],
) )
const onEditChange = (changedValues, allValues) => {
console.log('onEditChange', changedValues, '\n', allValues)
// const { name, value } = e.target;
// setBakData(prevData => ({ ...prevData, [name]: value }));
// debouncedSave(bakData);
}
useEffect(() => { useEffect(() => {
return () => { return () => {
if (idleCallbackId.current && window.cancelIdleCallback) { if (idleCallbackId.current && window.cancelIdleCallback) {
@ -716,6 +566,13 @@ const NewEmail = () => {
} }
} }
}, [debouncedSave]) }, [debouncedSave])
const onEditChange = (changedValues, allValues) => {
// console.log('onEditChange', changedValues, '\n', allValues)
if ('from' in changedValues) {
handleSwitchEmail(allValues.from);
}
}
return ( return (
<> <>
@ -735,14 +592,14 @@ const NewEmail = () => {
requiredMark={false} requiredMark={false}
// labelCol={{ span: 3 }} // labelCol={{ span: 3 }}
> >
<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'> <div className='w-full flex flex-wrap gap-2 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' size='middle' onClick={onHandleSaveOrSend} loading={sendLoading} icon={<SendOutlined />}> <Button type='primary' size='middle' onClick={onHandleSaveOrSend} loading={sendLoading} icon={<SendOutlined />}>
发送 发送
</Button> </Button>
<Form.Item noStyle name={'from'} rules={[{ required: true, message: '请选择发件地址' }]}> <Form.Item name={'from'} rules={[{ required: true, message: '请选择发件地址' }]} >
<Select labelInValue={false} options={emailListOption} labelRender={item => `发件人: ${item?.label || '选择'}`} variant={'borderless'} placeholder='发件人: 选择' className='[&_.ant-select-selection-item]:font-bold [&_.ant-select-selection-placeholder]:font-bold [&_.ant-select-selection-placeholder]:text-black' classNames={{popup: {root:'min-w-60'}}} /> <Select labelInValue={false} options={emailListOption} labelRender={item => `发件人: ${item?.label || '选择'}`} variant={'borderless'} placeholder='发件人: 选择' className='[&_.ant-select-selection-item]:font-bold [&_.ant-select-selection-placeholder]:font-bold [&_.ant-select-selection-placeholder]:text-black' classNames={{popup: {root:'min-w-60'}}} />
<div className="ant-form-item-explain-error text-red-500" >请选择发件地址</div>
</Form.Item> </Form.Item>
{/* <div className="ant-form-item-explain-error text-red-500" >请选择发件地址</div> */}
<div className='ml-auto'></div> <div className='ml-auto'></div>
<span>{orderDetail.order_no}</span> <span>{orderDetail.order_no}</span>
<span>{templateContent.mailtypeName}</span> <span>{templateContent.mailtypeName}</span>
@ -789,7 +646,7 @@ const NewEmail = () => {
</Form.Item> </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'> <Flex justify='space-between'>
<Upload {...uploadProps} name='file' className='w-full'> <Upload {...uploadProps} name='file' className='w-full [&_.ant-upload-list-item-name]:cursor-pointer'>
<Button icon={<UploadOutlined />}>附件</Button> <Button icon={<UploadOutlined />}>附件</Button>
</Upload> </Upload>
<Flex align={'center'} className='absolute right-0'> <Flex align={'center'} className='absolute right-0'>

@ -114,7 +114,7 @@ function Follow() {
subject: emailItem.MAI_Subject, subject: emailItem.MAI_Subject,
}, },
} }
console.log('emailItem', emailItem); // console.log('emailItem', emailItem);
setSelectedEmail(emailMsg) setSelectedEmail(emailMsg)
}; };

@ -1,8 +1,7 @@
import { createContext, useEffect, useMemo, useState } from 'react' import { useMemo } from 'react'
import { App, Flex, Button, Tooltip, List, Form, Row, Col, Dropdown, Input, Checkbox, DatePicker, Switch, Breadcrumb, Skeleton } from 'antd' import { App, Dropdown } from 'antd'
import useConversationStore from '@/stores/ConversationStore' import useConversationStore from '@/stores/ConversationStore'
import { emailTemplates, openPopup, useEmailTemplate } from '@/hooks/useEmail'; import { emailTemplates, openPopup } from '@/hooks/useEmail';
import { POPUP_FEATURES } from '@/config';
import { isEmpty } from '@/utils/commons'; import { isEmpty } from '@/utils/commons';
const NewEmailButton = ({ ...props }) => { const NewEmailButton = ({ ...props }) => {
@ -15,12 +14,12 @@ const NewEmailButton = ({ ...props }) => {
notification.warning({ message: '请选择到订单目录', placement: 'top' }) notification.warning({ message: '请选择到订单目录', placement: 'top' })
return false return false
} }
openPopup(`/email/new/0/${COLI_SN}/${key}`, `new-0-${COLI_SN}-${key}`, POPUP_FEATURES) openPopup(`/email/new/0/${COLI_SN}/${key}`, `new-0-${COLI_SN}-${key}`)
}; };
const handleNewEmail = () => { const handleNewEmail = () => {
console.info('新邮件') console.info('新邮件')
openPopup(`/email/new/0/${COLI_SN}`, `new-0-${COLI_SN}`, POPUP_FEATURES) openPopup(`/email/new/0/${COLI_SN}`, `new-0-${COLI_SN}`)
} }
return ( return (

Loading…
Cancel
Save