feat: 邮件快速回复, 纯文本, 无附件, 无引用

dev/emitter
Lei OT 10 months ago
parent 615f7b332f
commit 92d54d7a27

@ -21,7 +21,7 @@ export const salesSignature = async (opisn, lgc = 1) => {
* 发送邮件
*/
export const postSendEmail = async (body) => {
const { attaList, atta, content, ...bodyData } = body;
const { attaList=[], atta, content, ...bodyData } = body;
const formData = new FormData();
Object.keys(bodyData).forEach(function (key) {
formData.append(key, bodyData[key]);

@ -297,7 +297,8 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, toEma
};
/**
* 先推到消息记录上面, 再发送
* 保存成功, 推一个气泡
* 再从异步通知更新消息发送状态
*/
const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage);
const invokeEmailMessage = (msgObj) => {
@ -425,7 +426,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, fromOrder, toEma
<Form
form={form}
preserve={false}
name='conversation_filter_form'
name='email_max_form'
size='small'
layout={'inline'}
variant={'borderless'}

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { Button, ConfigProvider, Dropdown, Flex, Select } from 'antd'
import { DownOutlined } from '@ant-design/icons'
import { useState, useEffect, useRef } from 'react'
import { App, Button, ConfigProvider, Dropdown, Flex, Select, Input, Tooltip, Form } from 'antd'
import { DownOutlined, DollarOutlined, ExpandAltOutlined, ExpandOutlined, SendOutlined } from '@ant-design/icons'
import EmailEditorPopup from './EmailEditorPopup'
import useStyleStore from '@/stores/StyleStore'
import useAuthStore from '@/stores/AuthStore'
@ -8,8 +8,17 @@ import useAuthStore from '@/stores/AuthStore'
import useConversationStore from '@/stores/ConversationStore'
import { useOrderStore } from '@/stores/OrderStore'
import { EditIcon } from '@/components/Icons'
import { cloneDeep, isEmpty } from '@/utils/commons'
import InputEmoji from './Emoji'
import InputMediaUpload from './MediaUpload'
import { v4 as uuid } from 'uuid'
import { postSendEmail } from '@/actions/EmailActions'
import { sentMsgTypeMapped, } from '@/channel/bubbleMsgUtils';
const EmailNewBtn = ({ ...props }) => {
const { notification } = App.useApp()
const [form] = Form.useForm()
const [mobile] = useStyleStore((state) => [state.mobile])
const { userId, username, emailList, email } = useAuthStore((state) => state.loginUser)
@ -17,8 +26,10 @@ const EmailNewBtn = ({ ...props }) => {
const currentConversation = useConversationStore((state) => state.currentConversation)
// const talkabled = !isEmpty(currentConversation.sn) && websocketOpened;
const { orderDetail, customerDetail } = useOrderStore()
const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer])
const emailListOption = emailList?.map((ele) => ({ ...ele, label: ele.email, key: ele.email, value: ele.email })) || []
const emailListAddrMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.email]: v }), {});
const [pickEmail, setPickEmail] = useState({ key: email, email })
const [fromUser, setFromUser] = useState()
@ -47,40 +58,187 @@ const EmailNewBtn = ({ ...props }) => {
setToEmail(currentConversation?.channels?.email || customerDetail?.email || '')
}
const textInputRef = useRef(null)
const websocketOpened = useConversationStore((state) => state.websocketOpened)
const talkabled = !isEmpty(currentConversation.sn) && websocketOpened
const [sendLoading, setSendLoading] = useState(false)
/**
* 保存成功, 推一个气泡
* 再从异步通知更新消息发送状态
*/
const sentOrReceivedNewMessage = useConversationStore((state) => state.sentOrReceivedNewMessage);
const invokeEmailMessage = (msgObj) => {
const msgObjMerge = {
sender: 'me',
senderName: 'me',
msg_source: 'email',
// to: currentConversation.whatsapp_phone_number,
date: new Date(),
status: 'waiting', // accepted
...msgObj,
// id: `${currentConversation.sn}.${msgObj.id}`,
// id: `${stickToCid}.${msgObj.id}`,
conversationid: currentConversation.sn,
};
// olog('invoke upload', msgObjMerge)
const contentToRender = sentMsgTypeMapped[msgObjMerge.type].contentToRender(msgObjMerge);
// console.log(contentToRender, 'contentToRender sendMessage------------------');
sentOrReceivedNewMessage(contentToRender.conversationid, contentToRender);
};
const addEmoji = (emoji) => {
form.setFieldValue('mailcontent', form.getFieldValue('mailcontent') + emoji)
// setTextContent((prevValue) => {
// return prevValue + emoji
// })
}
// const [quickValidateHelp, setQuickValidateHelp] = useState('')
const handleSendEmail = async (values) => {
console.log('invoke email message');
const emailAccount = { opi_sn: fromUser, email: pickEmail.key, mat_sn: '' };
emailAccount.opi_sn = fromUser || emailListAddrMapped?.[emailAccount.email]?.opi_sn || '';
emailAccount.mat_sn = emailListAddrMapped?.[emailAccount.email]?.mat_sn || '';
const stickToCid = currentConversation.sn
const body = {}
body.subject = values.subject
body.mailcontent = values.mailcontent
body.from = emailAccount.email
body.to = currentConversation.channels?.email || customerDetail?.email || ''
// body.attaList = fileList;
body.opi_sn = emailAccount.opi_sn
body.mat_sn = emailAccount.mat_sn
body.coli_sn = currentConversation?.coli_sn || ''
// console.log('body', body, '\n')
body.cc = ''
body.bcc = ''
const msgObj = {
type: 'email',
id: uuid(),
from: body.from,
to: body.to,
cc: '',
bcc: '',
subject: values.subject,
content: body.mailcontent,
email: {
subject: values.subject,
content: body.mailcontent,
},
}
setSendLoading(true)
body.externalID = stickToCid
body.actionID = `${stickToCid}.${msgObj.id}`
try {
const bubbleMsg = cloneDeep(msgObj)
bubbleMsg.id = `${stickToCid}.${msgObj.id}`
bubbleMsg.content = undefined
// console.log('email message', body, '\n', bubbleMsg)
const result = await postSendEmail(body)
const mailSavedId = result.id || ''
bubbleMsg.email.mai_sn = mailSavedId
invokeEmailMessage(bubbleMsg)
form.resetFields();
} catch (error) {
notification.error({
message: '邮件保存失败',
description: error.message,
placement: 'top',
duration: 3,
})
} finally {
setSendLoading(false)
}
}
return (
<ConfigProvider theme={{ token: { colorPrimary: '#6366f1' } }}>
<Flex gap={8} className='p-2 bg-gray-200 rounded rounded-b-none border-gray-300 border-solid border-t border-b-0 border-x-0' align={'center'} justify={'flex-end'}>
{/* <span>新邮件:</span> */}
{/* <Dropdown.Button
// disabled={!talkabled}
menu={{
selectable: true,
items: emailListOption,
onClick: ({ key }) => {
const find = emailListOption?.find((ele) => ele.email === key)
setPickEmail(find)
// openEditor(key);
},
selectedKeys: [pickEmail?.email],
}}
// onClick={() => openEditor(pickEmail.key)}
type='primary'
className='w-auto'
icon={<DownOutlined />}>
<>{pickEmail?.email}</>
</Dropdown.Button> */}
{/* <span>:</span>
<Select
options={emailListOption}
labelInValue
value={pickEmail}
onChange={(val) => { setPickEmail(val); setFromUser(val.opi_sn)}}
// variant={'borderless'}
/> */}
<Button icon={<EditIcon />} type='primary' onClick={() => openEditor(pickEmail.key)}>
新邮件
</Button>
<Form
form={form}
preserve={false}
name='email_max_form'
layout={'inline'}
// initialValues={{}}
onFinish={handleSendEmail}
onFinishFailed={console.log}
className=''
>
<Form.Item name='from' className='hidden'></Form.Item>
<Form.Item name='to' className='hidden'></Form.Item>
<Form.Item name='cc' className='hidden'></Form.Item>
<Form.Item name='bcc' className='hidden'></Form.Item>
<Form.Item name='subject' className='w-full' rules={[{ required: true, message: '' }]}>
<Input
className='rounded-b-none border-b-0 font-bold text-base text-indigo-600'
placeholder='*主题'
disabled={!talkabled}
suffix={
<Tooltip title={'全文编辑'}>
<Button
type='text'
size='small'
onClick={() => openEditor(pickEmail.key)}
icon={<ExpandOutlined className='text-indigo-600' />}
/>
</Tooltip>
}
/>
</Form.Item>
<Form.Item name='mailcontent' className='w-full' rules={[{ required: true, message: '' }]}>
<Input.TextArea
allowClear
ref={textInputRef}
size='large'
maxLength={2000}
showCount={true}
placeholder={!talkabled ? '请先选择会话' : '*纯文本邮件'}
rows={2}
disabled={!talkabled}
className='rounded-none emoji'
autoSize={{ minRows: 2, maxRows: 6 }}
/>
</Form.Item>
<Flex gap={8} className='w-full bg-gray-200 p-1 rounded-b-0' align={'center'} justify={'space-between'}>
<Flex gap={4} className='*:text-primary *:rounded-none items-center'>
<InputEmoji key='emoji' disabled={!talkabled} inputEmoji={addEmoji} />
{/* <InputMediaUpload key={'addNewMedia'} disabled={!textabled} {...{ invokeUploadFileMessage, invokeSendUploadMessage }} /> */}
<Tooltip title='支付链接' >
<Button type='text' onClick={() => openPaymentDrawer()} icon={<DollarOutlined className='text-orange-500' />} size={'middle'} />
</Tooltip>
</Flex>
{/* <span>切换邮箱:</span>*/}
<Flex gap={4} align={'center'}>
<div className='text-red-500'>
{/* <div>{textPlaceHolder}</div> */}
{/* {quickValidateHelp} */}
</div>
<Select
disabled={!talkabled}
// size={'small'}
options={emailListOption}
labelInValue
value={pickEmail}
onChange={(val) => {
setPickEmail(val)
setFromUser(val.opi_sn)
}}
// variant={'borderless'}
/>
<Button icon={<SendOutlined />} type='primary' htmlType={'submit'} disabled={!talkabled} loading={sendLoading} _onClick={() => handleSendEmail()}>
发送
</Button>
{/* <Button icon={<EditIcon />} type='primary' onClick={() => openEditor(pickEmail.key)}>
新邮件
</Button> */}
</Flex>
</Flex>
</Form>
<EmailEditorPopup
{...{ open, setOpen }}
fromEmail={fromEmail}
@ -91,7 +249,6 @@ const EmailNewBtn = ({ ...props }) => {
action='new'
key={'email-new-editor-popup'}
/>
</Flex>
</ConfigProvider>
)
}

Loading…
Cancel
Save