加载模板

dev/ckeditor
Lei OT 3 weeks ago
parent 1393bf9899
commit e51581202d

@ -327,14 +327,23 @@ export const updateEmailAction = async (params = { mai_sn_list: [], set: {} }) =
/**
* 获取邮件模板
* @param {object} params - Parameters for the email template request.
* @param {number} [params.coli_sn] - Customer order line item serial number.
* @param {number} [params.lgc] - Language code.
* @param {number} [params.opi_sn] - Order product item serial number.
* @param {string} [params.remind_type] - Type of reminder.
* @param {number} [params.remind_index] - Index of the reminder.
*/
export const getEmailTemplateAction = async (template_name, params = { coli_sn: 0, lgc: 1 }) => {
const { errcode, result } = await fetchJSON(`${EMAIL_HOST}/email/template/${template_name}`, params)
return errcode === 0 ? {} : result
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 { html, bodyContent, bodyText } = parseHTMLString(result?.MailContent, true) ;
return errcode === 0 ? {...result, bodyContent }: {}
}
/**
* 保存邮件草稿
* @param {object} body - The body of the email.
* @param {boolean} [isDraft=false] - Whether the email is a draft.
*/
export const saveEmailDraftOrSendAction = async (body, isDraft = false) => {
const url = isDraft !== false ? `${API_HOST}/v3/email_draft_save` : `${EMAIL_HOST}/sendmail`;

@ -6,7 +6,7 @@
// export const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_144'; // prod: Slave
// debug:
// export const API_HOST = 'http://202.103.68.144:8889/v2';
export const API_HOST = 'http://202.103.68.144:8889/v2';
// export const WS_URL = 'ws://202.103.68.144:8888';
// export const EMAIL_HOST = 'http://202.103.68.231:888/service-mail';
// export const WAI_HOST = 'http://47.83.248.120/api/v1'; // 香港服务器
@ -18,7 +18,7 @@ export const EMAIL_ATTA_HOST = 'https://p9axztuwd7x8a7.mycht.cn/attachment'; //
// prod:
// export const WAI_HOST = 'https://wai-server-qq4qmtq7wc9he4.mycht.cn/api/v1';
export const EMAIL_HOST = 'https://p9axztuwd7x8a7.mycht.cn/mail-server/service-mail';
export const API_HOST = 'https://p9axztuwd7x8a7.mycht.cn/whatsapp_server/v2';
// export const API_HOST = 'https://p9axztuwd7x8a7.mycht.cn/whatsapp_server/v2';
export const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_server'; // prod:
export const VONAGE_URL = 'https://p9axztuwd7x8a7.mycht.cn/vonage-server'; // 语音和视频接口:
export const HT3 = process.env.NODE_ENV === 'production' ? 'https://p9axztuwd7x8a7.mycht.cn/ht3' : 'https://p9axztuwd7x8a7.mycht.cn/ht3';

@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from 'react'
import { isEmpty, readIndexDB, } from '@/utils/commons'
import { getEmailDetailAction, postResendEmailAction, getSalesSignatureAction, getEmailOrderAction, queryEmailListAction } from '@/actions/EmailActions'
import { isEmpty, objectMapper, olog, readIndexDB, } from '@/utils/commons'
import { getEmailDetailAction, postResendEmailAction, getSalesSignatureAction, getEmailOrderAction, queryEmailListAction, getEmailTemplateAction } from '@/actions/EmailActions'
import { App } from 'antd'
import useConversationStore from '@/stores/ConversationStore';
import { msgStatusRenderMapped } from '@/channel/bubbleMsgUtils';
@ -11,7 +11,7 @@ export const useEmailSignature = (opi_sn) => {
const [signature, setSignature] = useState('')
const getSignature = useCallback(async () => {
if (isEmpty(opi_sn)) {
if (isEmpty(Number(opi_sn))) {
return false
}
try {
@ -24,9 +24,9 @@ export const useEmailSignature = (opi_sn) => {
useEffect(() => {
getSignature()
}, [opi_sn])
}, [getSignature])
return { signature, getSignature }
return { signature }
}
@ -35,18 +35,18 @@ export const useEmailSignature = (opi_sn) => {
* @param data 直接传递, 不重复获取
* * 在详情点击`回复`呼出编辑时
*/
export const useEmailDetail = (mai_sn, data) => {
export const useEmailDetail = (mai_sn, data={}, oid=0) => {
const {notification} = App.useApp()
const [loading, setLoading] = useState(false)
const [mailData, setMailData] = useState({ loading, info: {}, content: '', attachments: [], AttachList: [] })
const [coliSN, setColiSN] = useState('');
const [mailData, setMailData] = useState({ loading, info: { MAI_COLI_SN: 0 }, content: '', attachments: [], AttachList: [] })
const [coliSN, setColiSN] = useState(oid);
const [orderDetail, setOrderDetail] = useState({});
const [updateMessageItem] = useConversationStore(state => [state.updateMessageItem]);
useEffect(() => {
const getEmailDetail = async () => {
if (isEmpty(mai_sn)) {
if (isEmpty(Number(mai_sn))) {
return false
}
try {
@ -182,6 +182,117 @@ export const useEmailList = (mailboxDirNode) => {
return { loading, isFreshData, error, mailList, refresh }
}
const orderMailTypes = new Map([
['48001', '发送一催'],
['48002', '发送二催'],
['48003', '发送三催'],
['48004', '发送已收客人付款通知'],
['48005', '发送FAQ及PreSurvey'],
['48006', '发送降价邮件'],
['48007', '发送亚马逊津贴'],
['48008', '发送报价信'],
['48009', '提交调度'],
['48010', '发送确认信'],
['48011', '发送余款提醒信'],
['48012', '发送入境提醒'],
['48013', '首站关怀'],
['48014', '抵桂面谈'],
['48015', '末站关怀'],
['48016', '发送计划'],
['48017', '报价中'],
['48018', '以后联系'],
['48019', '成行'],
['48020', '团款收讫'],
['48021', '取消'],
['48022', '丢失'],
['48023', '已做计划'],
['48024', '一催回复'],
['48025', '二催回复'],
['48026', '第一次报价回复'],
['48027', '发送商务感谢信'],
['48028', '商务酒店房满邮件'],
['48029', '商务预订传真'],
['48030', '商务取消邮件'],
['48031', '商务单团财务表'],
['48032', '商务收款单'],
['48033', '商务退款单'],
['48034', '商务财务转帐申请'],
['48035', '商务未订先确认'],
['48036', '商务等待确认'],
['48037', '商务已确认'],
['48038', '商务已付款'],
['48039', '商务成功订单'],
['48040', '商务取消订单'],
['48041', '商务不成行订单'],
['48042', '商务已付款未出票'],
['48043', '商务已付款并出票'],
['48044', '商务无效订单'],
['48045', '问候提醒'],
['48046', '发送邮件'],
['48047', '来华一周年问候'],
['48048', '来华二周年提醒'],
['48049', 'CH Phone询问邮件'],
['48050', '订单电话销售'],
['48051', '等待付订金'],
['48052', '商务订单预订中'],
['48053', '客户需求调查'],
['48054', '发送TourCredits通知邮件'],
['48055', 'GH海外PostSurvey'],
])
export const emailTemplates = [
{ key: '', value: '', label: ''},
{ type: 'RemindOneWL', index: 1, key: '1@RemindOneWL', value: '1@RemindOneWL', label: '一催模板一,询问客人是否收到报价信' },
{ type: 'RemindOneWL', index: 2, key: '2@RemindOneWL', value: '2@RemindOneWL', label: '一催模板二,询问客人是否修改行程' },
{ type: 'divider' },
{ type: 'RemindTwoWL', index: 1, key: '1@RemindTwoWL', value: '1@RemindTwoWL', label: '二催模板一,询问客人对行程的看法' },
{ type: 'RemindTwoWL', index: 2, key: '2@RemindTwoWL', value: '2@RemindTwoWL', label: '二催模板二,表达服务的意识' },
{ type: 'divider' },
{ type: 'RemindThreeWL', index: 1, key: '1@RemindThreeWL', value: '1@RemindThreeWL', label: '三催模板三,强调价格有效期' },
];
export const emailTemplateMap = emailTemplates.reduce((acc, cur) => {
if (cur.type === 'divider') {
return acc
}
acc[cur.key] = cur
return acc
}, {});
/**
*
* @param {object} params - Parameters for the email template request.
* @param {number} [params.coli_sn] - Customer order line item serial number.
* @param {number} [params.lgc] - Language code.
* @param {number} [params.opi_sn] - Order product item serial number.
*/
export const useEmailTemplate = (templateKey, params) => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [templateContent, setTemplateContent] = useState({ mailtype: '', mailtypeName: '', subject: '', mailcontent: '' })
const getTemplateContent = useCallback(async () => {
if (!templateKey || !Number(params.coli_sn) || !Number(params.opi_sn)) {
setTemplateContent({ mailtype: '', mailtypeName: '', subject: '', mailcontent: '' })
setLoading(false)
setError(null)
return
}
setLoading(true)
setError(null)
try {
const { index: remind_index, type: remind_type } = emailTemplateMap[templateKey] || {};
const _params = { ...params, remind_index, remind_type};
const x = await getEmailTemplateAction(_params)
const lowerCaseShallow = Object.keys(x).reduce((acc, key) => ({...acc, [key.toLowerCase()]: x[key]}), {})
setTemplateContent({...lowerCaseShallow, mailtypeName: orderMailTypes.get(lowerCaseShallow.mailtype)})
} catch (networkError) {
setError(new Error(`Failed to get template content: ${networkError.message}.`))
} finally {
setLoading(false)
}
}, [templateKey, params.opi_sn, params.coli_sn, params.lgc])
useEffect(() => {
getTemplateContent()
}, [getTemplateContent])
return { loading, error, templateContent };
}

@ -87,8 +87,10 @@ const router = createBrowserRouter([
{ path: 'customer_relation/index', element: <CustomerRelation /> },
],
},
{ path: 'email/:action/:quoteid/:oid/:templateKey', element: <NewEmail />},
{ path: 'email/:action/:quoteid/:oid', element: <NewEmail />},
{ path: 'email/:action/:quoteid', element: <NewEmail />},
{ path: 'email/new', element: <NewEmail />},
// { path: 'email/new/0/:oid', element: <NewEmail />},
],
},
{

@ -1,6 +1,6 @@
import { useEffect, useState, useRef, useCallback } from 'react'
import { useEffect, useState, useRef, useCallback, useMemo } from 'react'
import { useParams, useNavigate, useLocation } from 'react-router-dom';
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs, Radio } from 'antd'
import { App, ConfigProvider, Button, Form, Input, Flex, Checkbox, Popconfirm, Select, Space, Upload, Divider, Modal, Tabs, Radio, Typography } from 'antd'
import { UploadOutlined, LoadingOutlined, SaveOutlined, SendOutlined, CheckCircleOutlined } from '@ant-design/icons'
import '@dckj/react-better-modal/dist/index.css'
import useStyleStore from '@/stores/StyleStore'
@ -10,11 +10,11 @@ import useAuthStore from '@/stores/AuthStore'
import LexicalEditor from '@/components/LexicalEditor'
import { v4 as uuid } from 'uuid'
import { cloneDeep, debounce, isEmpty, writeIndexDB, readIndexDB, deleteIndexDBbyKey } from '@/utils/commons'
import { cloneDeep, debounce, isEmpty, writeIndexDB, readIndexDB, deleteIndexDBbyKey, olog } from '@/utils/commons'
import '@/views/Conversations/Online/Input/EmailEditor.css'
import { parseHTMLString, postSendEmail, saveEmailDraftOrSendAction } from '@/actions/EmailActions'
import { sentMsgTypeMapped } from '@/channel/bubbleMsgUtils'
import { EmailBuilder, useEmailDetail, useEmailSignature } from '@/hooks/useEmail'
import { EmailBuilder, useEmailDetail, useEmailSignature, useEmailTemplate } from '@/hooks/useEmail'
import useSnippetStore from '@/stores/SnippetStore'
// import { useOrderStore } from '@/stores/OrderStore'
import PaymentlinkBtn from '@/views/Conversations/Online/Input/PaymentlinkBtn'
@ -70,39 +70,31 @@ const generateQuoteContent = (mailData, isRichText = true) => {
const generateMailContent = (mailData) => `<br><p>${mailData.content}</p><br>`
/**
* 独立窗口编辑器
*
* - 从销售平台进入: 自动复制 storage, 可读取loginUser
*
* ! 无状态管理
*/
const NewEmail = () => {
const pageParam = useParams();
const editorKey = pageParam.action==='new' ? `new-${Date.now().toString(32)}` : `${pageParam.action}-${pageParam.quoteid}`
const { templateKey } = pageParam
const editorKey = pageParam.action==='new' ? `new-0-${pageParam.oid}` : `${pageParam.action}-${pageParam.quoteid}`
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 }), {})
const emailListOption = useMemo(() => emailList?.map((ele) => ({ ...ele, label: ele.email, key: ele.email, value: ele.email })) || [], [emailList])
const emailListOPIMapped = useMemo(() => emailListOption?.reduce((r, v) => ({ ...r, [v.opi_sn]: v }), {}), [emailListOption]);
const emailListAddrMapped = useMemo(() => emailListOption?.reduce((r, v) => ({ ...r, [v.email]: v }), {}), [emailListOption])
const emailListMatMapped = useMemo(() => emailListOption?.reduce((r, v) => ({ ...r, [v.mat_sn]: v }), {}), [emailListOption])
// 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(editorKey) || {})
// const { fromEmail, fromUser, fromOrder, oid, toEmail, conversationid, quoteid, initial = {}, mailData: _mailData, action = 'reply', draft = {}, receiverName, ...props } = emailEdiorProps.get(currentEditKey) || {};
const mai_sn = pageParam.quoteid // activeEdit.quoteid
const { loading: getLoading, mailData, orderDetail } = useEmailDetail(mai_sn)
const { loading: quoteLoading, mailData, orderDetail } = 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 { signature } = useEmailSignature(orderDetail.opi_sn || mailData.info?.MAI_OPI_SN || 0)
const [newFromEmail, setNewFromEmail] = useState('')
const [newToEmail, setNewToEmail] = useState('')
@ -121,35 +113,198 @@ const NewEmail = () => {
const [contentPrefix, setContentPrefix] = useState('')
const [localDraft, setLocalDraft] = useState();
const readMailboxLocalCache = async () => {
const readCache = await readIndexDB(editorKey, 'draft', 'mailbox')
if (readCache) {
const btn = (
<Space>
<Button type='link' size='small' onClick={() => notification.destroy()}>
关闭
</Button>
{/* <Button type="primary" size="small" onClick={() => notification.destroy()}>
Confirm
</Button> */}
</Space>
)
// notification.open({
// key: editorKey,
// placement: 'top',
// // message: '',
// description: '',
// duration: 0,
// actions: btn,
// })
setLocalDraft(readCache);
// const readMailboxLocalCache = async () => {
// console.log('===============', 'readMailboxLocalCache')
// const readCache = await readIndexDB(editorKey, 'draft', 'mailbox')
// if (readCache) {
// const btn = (
// <Space>
// <Button type='link' size='small' onClick={() => notification.destroy()}>
//
// </Button>
// {/* <Button type="primary" size="small" onClick={() => notification.destroy()}>
// Confirm
// </Button> */}
// </Space>
// )
// // notification.open({
// // key: editorKey,
// // placement: 'top',
// // // message: '',
// // description: '',
// // duration: 0,
// // actions: btn,
// // })
// setLocalDraft(readCache)
// if (!isEmpty(localDraft)) {
// const { htmlContent, ...draftFormsValues } = localDraft
// const _findMatOld = emailListMatMapped?.[draftFormsValues.mat_sn]
// const _from = draftFormsValues?.from || _findMatOld?.email || ''
// form.setFieldsValue(draftFormsValues)
// setNewFromEmail(_from)
// setEmailOPI(draftFormsValues.opi_sn)
// setEmailMat(draftFormsValues.mat_sn)
// setEmailOrder(draftFormsValues.coli_sn)
// requestAnimationFrame(() => {
// setInitialContent(htmlContent)
// })
// return false
// }
// }
// }
// useEffect(() => {
// readMailboxLocalCache()
// return () => {}
// }, [])
//
// -
// -
useEffect(() => {
console.log('useEffect 1---- \nform.setFieldsValue ');
if (isEmpty(mailData.content) && isEmpty(orderDetail.order_no)) {
return () => {}
}
}
const docTitle = mailData.info?.MAI_Subject || 'New Email-';
document.title = docTitle
const { info } = mailData
const { ...templateFormValues } = templateContent;
const orderReceiver = orderDetail.contact?.[0]?.email || ''
const _findMatOld = emailListOPIMapped?.[orderDetail.opi_sn]
const orderSender = _findMatOld?.email || ''
const _findMatOldE = emailListMatMapped?.[info.MAI_MAT_SN]
const quotedMailSender = _findMatOldE?.email || ''
const sender = quotedMailSender || orderSender
const quotedMailSenderObj = sender; // { key: sender, label: sender, value: sender }
const defaultMAT = emailListAddrMapped?.[sender]?.mat_sn || ''
const _form2 = {
coli_sn: pageParam.oid || info?.coli_sn || '',
mat_sn: info?.MAI_MAT_SN || defaultMAT,
opi_sn: info?.MAI_OPI_SN || orderDetail.opi_sn || '',
}
let readyToInitialContent = '';
let _formValues = {};
// 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) && !['edit', 'new'].includes(pageParam.action)) {
readyToInitialContent = contentPrefix + signatureBody
}
switch (pageParam.action) {
case 'reply':
_formValues = {
from: quotedMailSenderObj,
to: info?.replyTo || orderReceiver,
cc: info?.MAI_CS || '',
// bcc: quote.bcc || '',
subject: `Re: ${info.MAI_Subject || ''}`,
..._form2
}
break
case 'replyall':
_formValues = {
from: quotedMailSenderObj,
to: info?.replyToAll || orderReceiver,
cc: info?.MAI_CS || '',
// bcc: quote.bcc || '',
subject: `Re: ${info.MAI_Subject || ''}`,
..._form2
}
break
case 'forward':
_formValues = {
from: quotedMailSenderObj,
subject: `Fw: ${info.MAI_Subject || ''}`,
// coli_sn: pageParam.oid,
..._form2
}
break
case 'edit':
_formValues = {
from: quotedMailSenderObj,
to: info?.MAI_To || '',
cc: info?.MAI_CS || '',
subject: `Fw: ${info.MAI_Subject || ''}`,
id: pageParam.quoteid,
mai_sn: pageParam.quoteid,
..._form2
}
readyToInitialContent = generateMailContent(mailData)
break
case 'new':
_formValues = {
...templateFormValues,
from: quotedMailSenderObj,
to: orderReceiver,
..._form2
}
readyToInitialContent = generateMailContent({ content: templateContent.bodycontent })
break
default:
break
}
olog('222', _formValues, pageParam.action)
form.setFieldsValue(_formValues) // todo: from
setInitialContent(readyToInitialContent);
return () => {}
}, [orderDetail.order_no, quoteLoading, loadingTamplate])
// const readFromTemplate = () => {
// const { mailcontent, ...templateFormValues } = templateContent;
// if (mailcontent) {
// const _findMatOld = emailListOPIMapped?.[orderDetail.opi_sn]
// const _from = _findMatOld?.email || ''
// form.setFieldsValue({...templateFormValues, to: orderDetail?.contact?.[0]?.email || '', from1: { key: _from, value: _from, label: _from}});
// // setNewFromEmail(_from);
// // setEmailOPI(draftFormsValues.opi_sn)
// requestAnimationFrame(() => {
// setInitialContent(mailcontent);
// });
// }
// }
useEffect(() => {
readMailboxLocalCache()
// readMailboxLocalCache()
if (loadingTamplate) {
notification.open({
key: editorKey,
placement: 'top',
// message: '',
description: '正在加载邮件模板...',
duration: 0,
icon: <LoadingOutlined />,
// actions: btn,
// closeIcon: null,
closable: false,
})
} else {
notification.destroy(editorKey)
}
// readFromTemplate();
return () => {}
}, [])
}, [loadingTamplate])
@ -157,6 +312,8 @@ const NewEmail = () => {
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])
@ -169,7 +326,6 @@ const NewEmail = () => {
setEmailOPI(_findMat?.opi_sn)
}
const { signature } = useEmailSignature(emailOPI)
const [isRichText, setIsRichText] = useState(mobile === false)
// const [isRichText, setIsRichText] = useState(false); //
@ -193,11 +349,11 @@ const NewEmail = () => {
form.setFieldValue('content', htmlContent)
const { bodyText: abstract } = parseHTMLString(htmlContent, true);
const body = {
from: newFromEmail,
attaList: fileList,
opi_sn: emailOPI,
mat_sn: emailMat,
coli_sn: emailOrder || '',
// from: newFromEmail,
// attaList: fileList,
// opi_sn: emailOPI,
// mat_sn: emailMat,
// coli_sn: emailOrder || '',
}
// form.setFieldValue('abstract', getAbstract(textContent))
debouncedSave({ ...form.getFieldsValue(), ...body, htmlContent, abstract, })
@ -205,129 +361,111 @@ const NewEmail = () => {
const [initialContent, setInitialContent] = useState('')
const [showQuoteContent, setShowQuoteContent] = useState(false)
const [mergeQuote, setMergeQuote] = useState(true)
// const [mergeQuote, setMergeQuote] = useState(true)
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(localDraft)) {
const {htmlContent, ...draftFormsValues} = localDraft;
const _findMatOld = emailListMatMapped?.[draftFormsValues.mat_sn]
const _from = draftFormsValues?.from || _findMatOld?.email || ''
form.setFieldsValue(draftFormsValues);
setNewFromEmail(_from);
setEmailOPI(draftFormsValues.opi_sn)
setEmailMat(draftFormsValues.mat_sn)
setEmailOrder(draftFormsValues.coli_sn)
requestAnimationFrame(() => {
setInitialContent(htmlContent);
});
return () => {};
}
//
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, localDraft])
// 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 handlePlainTextOpenChange = ({ target }) => {
@ -470,11 +608,11 @@ const NewEmail = () => {
const onHandleSaveOrSend = async (isDraft = false) => {
// console.log('onSend callback', '\nisRichText', isRichText);
// console.log(form.getFieldsValue());
console.log(form.getFieldsValue());
const body = structuredClone(form.getFieldsValue())
body.mailtype = ''; // todo:
// body.mailtype = ''; // todo:
// body.id = ''; // todo: 稿
body.from = newFromEmail
// body.from = newFromEmail // todo:
body.attaList = fileList
body.opi_sn = emailOPI
body.mat_sn = emailMat
@ -513,7 +651,7 @@ const NewEmail = () => {
// console.log('postSendEmail', body, '\n', msgObj);
// return;
// todo: 稿,
// todo: 稿, ; ID
const result = await saveEmailDraftOrSendAction(body, isDraft)
const mailSavedId = result.id || ''
bubbleMsg.email.mai_sn = mailSavedId
@ -582,12 +720,32 @@ const NewEmail = () => {
return (
<>
<ConfigProvider theme={{ token: { colorPrimary: '#6366f1' } }}>
<Form
form={form}
onValuesChange={onEditChange}
// onFinishFailed={onFinishFailed}
preserve={false}
name={`email_max_form`}
size='small'
layout={'inline'}
variant={'borderless'}
// initialValues={{}}
// onFinish={() => {}}
className='email-editor-wrapper *:mb-2 *:border-b *:border-t-0 *:border-x-0 *:border-indigo-100 *:border-solid '
requiredMark={false}
// 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'>
<Button type='primary' onClick={onHandleSaveOrSend} loading={sendLoading} icon={<SendOutlined />}>
<Button type='primary' size='middle' onClick={onHandleSaveOrSend} 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' />
<Form.Item noStyle 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'}}} />
<div className="ant-form-item-explain-error text-red-500" >请选择发件地址</div>
</Form.Item>
<div className='ml-auto'></div>
<span>{orderDetail.order_no}</span>
<span>{templateContent.mailtypeName}</span>
<Popconfirm trigger1={['hover', 'click']}
description='切换内容为纯文本格式将丢失信件和签名的格式, 确定使用纯文本?'
onConfirm={confirmPlainText}
@ -601,20 +759,6 @@ const NewEmail = () => {
</Popconfirm>
<Button onClick={() => onHandleSaveOrSend(true)} type='dashed' icon={<SaveOutlined />} size='small' className='' >存草稿</Button>
</div>
<Form
form={form}
onValuesChange={onEditChange}
preserve={false}
name={`email_max_form-${Date.now().toString(32)}`}
size='small'
layout={'inline'}
variant={'borderless'}
// initialValues={{}}
// onFinish={() => {}}
className='email-editor-wrapper *:mb-2 *:border-b *:border-t-0 *:border-x-0 *:border-indigo-100 *:border-solid '
requiredMark={false}
// labelCol={{ span: 3 }}
>
<Form.Item className='w-full'>
<Space.Compact className='w-full'>
<Form.Item name={'to'} label='收件人' rules={[{ required: true }]} className='!flex-1'>
@ -675,9 +819,24 @@ const NewEmail = () => {
<Form.Item name='id' hidden>
<Input />
</Form.Item>
<Form.Item name='mai_sn' hidden>
<Input />
</Form.Item>
<Form.Item name='mat_sn' hidden>
<Input />
</Form.Item>
<Form.Item name='coli_sn' hidden>
<Input />
</Form.Item>
<Form.Item name='opi_sn' hidden>
<Input />
</Form.Item>
<Form.Item name='mailtype' hidden>
<Input />
</Form.Item>
</Form>
<LexicalEditor {...{ isRichText }} onChange={handleEditorChange} defaultValue={initialContent} />
{pageParam.quoteid && pageParam.action!=='edit' && !showQuoteContent && (
{!isEmpty(Number(pageParam.quoteid)) && pageParam.action!=='edit' && !showQuoteContent && (
<div className='flex justify-start items-center ml-2'>
<Button className='flex gap-2 ' type='link' onClick={() => setShowQuoteContent(!showQuoteContent)}>
显示引用内容 {/*(不可更改)*/}

@ -5,7 +5,8 @@ import dayjs from 'dayjs'
import { useEmailList } from '@/hooks/useEmail'
import { isEmpty } from '@/utils/commons'
import { MailboxDirIcon } from './MailboxDirIcon'
import { AttachmentIcon } from '@/components/Icons'
import { AttachmentIcon, MailCheckIcon } from '@/components/Icons'
import NewEmailButton from './NewEmailButton'
const { RangePicker } = DatePicker
@ -124,49 +125,7 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => {
return (
<>
<div className='bg-white h-auto px-1 flex gap-1 items-center'>
<Dropdown.Button
className='w-auto'
placement='bottom'
arrow
type={'primary'}
menu={{
items: [
{
key: '1',
label: '一催模板一,询问客人是否收到报价信',
},
{
key: '2',
label: '一催模板二,询问客人是否修改行程',
},
{
type: 'divider',
},
{
key: '3',
label: '二催模板一,询问客人对行程的看法',
},
{
key: '4',
label: '二催模板二,表达服务的意识',
},
{
type: 'divider',
},
{
key: '5',
label: '三催模板三,强调价格有效期',
},
],
onClick: (item) => {
console.info('menu', item)
},
}}
onClick={() => {
console.info('新邮件')
}}>
新邮件
</Dropdown.Button>
<NewEmailButton />
<Flex wrap gap='middle' justify={'center'} className='min-w-40'>
<Tooltip title='全选'>
<Checkbox></Checkbox>
@ -175,7 +134,7 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => {
<Button shape='circle' type='text' size='small' icon={<ReadOutlined />} />
</Tooltip>
<Tooltip title='已处理'>
<Button shape='circle' type='text' size='small' icon={<CheckSquareOutlined />} />
<Button shape='circle' type='text' size='small' icon={<MailCheckIcon />} />
</Tooltip>
<Tooltip title='刷新'>
<Button shape='circle' type='text' size='small' icon={<ReloadOutlined />} onClick={refresh} />

@ -0,0 +1,38 @@
import { createContext, useEffect, useMemo, useState } from 'react'
import { Flex, Button, Tooltip, List, Form, Row, Col, Dropdown, Input, Checkbox, DatePicker, Switch, Breadcrumb, Skeleton } from 'antd'
import useConversationStore from '@/stores/ConversationStore'
import { emailTemplates, openPopup, useEmailTemplate } from '@/hooks/useEmail';
import { POPUP_FEATURES } from '@/config';
const NewEmailButton = ({ ...props }) => {
const [mailboxActiveNode, setMailboxActiveNode] = useConversationStore((state) => [state.mailboxActiveNode, state.setMailboxActiveNode])
const COLI_SN = useMemo(() => mailboxActiveNode?.COLI_SN || 0, [mailboxActiveNode.COLI_SN])
const handleTemplateDropdown = ({ key, domEvent }) => {
console.log(key)
openPopup(`/email/new/0/${COLI_SN}/${key}`, `new-0-${COLI_SN}-${key}`, POPUP_FEATURES)
};
const handleNewEmail = () => {
console.info('新邮件')
openPopup(`/email/new/0/${COLI_SN}`, `new-0-${COLI_SN}`, POPUP_FEATURES)
}
return (
<>
<Dropdown.Button
className={`w-auto ${props.className}`}
placement='bottom'
arrow
type={'primary'}
menu={{
items: emailTemplates,
onClick: handleTemplateDropdown,
}}
onClick={handleNewEmail}>
新邮件
</Dropdown.Button>
</>
)
}
export default NewEmailButton
Loading…
Cancel
Save