feat: 邮件编辑: 切换纯文本; 切换发件人; 消息筛选

dev/email
Lei OT 12 months ago
parent 912c49cddd
commit bdedbc4781

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 3C4.5313 3 4.12549 3.32553 4.02381 3.78307L2.02381 12.7831C2.00799 12.8543 2 12.927 2 13V20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20V13C22 12.927 21.992 12.8543 21.9762 12.7831L19.9762 3.78307C19.8745 3.32553 19.4687 3 19 3H5ZM19.7534 12H15C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12H4.24662L5.80217 5H18.1978L19.7534 12Z"></path></svg>

After

Width:  |  Height:  |  Size: 455 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.02381 3.78307C4.12549 3.32553 4.5313 3 5 3H19C19.4687 3 19.8745 3.32553 19.9762 3.78307L21.9762 12.7831C21.992 12.8543 22 12.927 22 13V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V13C2 12.927 2.00799 12.8543 2.02381 12.7831L4.02381 3.78307ZM5.80217 5L4.24662 12H9C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12H19.7534L18.1978 5H5.80217ZM16.584 14C15.8124 15.7659 14.0503 17 12 17C9.94968 17 8.1876 15.7659 7.41604 14H4V19H20V14H16.584Z"></path></svg>

After

Width:  |  Height:  |  Size: 565 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 5.5V3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V19H20V7.3L12 14.5L2 5.5ZM0 10H5V12H0V10ZM0 15H8V17H0V15Z"></path></svg>

After

Width:  |  Height:  |  Size: 320 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21 3C21.5523 3 22 3.44772 22 4V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V19H20V7.3L12 14.5L2 5.5V4C2 3.44772 2.44772 3 3 3H21ZM8 15V17H0V15H8ZM5 10V12H0V10H5ZM19.5659 5H4.43414L12 11.8093L19.5659 5Z"></path></svg>

After

Width:  |  Height:  |  Size: 340 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M1.94619 9.31543C1.42365 9.14125 1.41953 8.86022 1.95694 8.68108L21.0431 2.31901C21.5716 2.14285 21.8747 2.43866 21.7266 2.95694L16.2734 22.0432C16.1224 22.5716 15.8178 22.59 15.5945 22.0876L12 14L18 6.00005L10 12L1.94619 9.31543Z"></path></svg>

After

Width:  |  Height:  |  Size: 334 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.7267 2.95694L16.2734 22.0432C16.1225 22.5716 15.7979 22.5956 15.5563 22.1126L11 13L1.9229 9.36919C1.41322 9.16532 1.41953 8.86022 1.95695 8.68108L21.0432 2.31901C21.5716 2.14285 21.8747 2.43866 21.7267 2.95694ZM19.0353 5.09647L6.81221 9.17085L12.4488 11.4255L15.4895 17.5068L19.0353 5.09647Z"></path></svg>

After

Width:  |  Height:  |  Size: 399 B

@ -6,6 +6,10 @@ import AttachmentLineSVG from '@/assets/icons/attachment-line.svg?react';
import AttachmentFillSVG from '@/assets/icons/attachment-fill.svg?react';
// import ShareForwardFillSVG from '@/assets/icons/share-forward-fill.svg?react';
import ShareForwardLineSVG from '@/assets/icons/share-forward-line.svg?react';
import InboxSVG from '@/assets/icons/inbox-2-fill.svg?react';
import MailSendFillSVG from '@/assets/icons/mail-send-fill.svg?react';
import SendPlaneFillSVG from '@/assets/icons/send-plane-fill.svg?react';
import SendPlaneLineSVG from '@/assets/icons/send-plane-line.svg?react';
export const ReplyIcon = (props) => <Icon component={ReplyLineSVG} {...props} />;
@ -13,6 +17,10 @@ export const ReplyAllIcon = (props) => <Icon component={ReplyAllLineSVG} {...pro
export const AttachmentIcon = (props) => <Icon component={AttachmentLineSVG} {...props} />;
export const AttachmentFillIcon = (props) => <Icon component={AttachmentFillSVG} {...props} />;
export const ShareForwardIcon = (props) => <Icon component={ShareForwardLineSVG} {...props} />;
export const InboxIcon = (props) => <Icon component={InboxSVG} {...props} />;
export const MailSendIcon = (props) => <Icon component={MailSendFillSVG} {...props} />;
export const SendPlaneFillIcon = (props) => <Icon component={SendPlaneFillSVG} {...props} />;
export const SendPlaneLineIcon = (props) => <Icon component={SendPlaneLineSVG} {...props} />;
const WABSvg = () => (
<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' width='16' height='16'>

@ -100,7 +100,7 @@ const EmailDetail = ({ open, setOpen, emailDetail, ...props }) => {
回复
</Button>
</div> */}
{/* <div className=' font-bold'>{emailOrigin.subject}</div> */}
<div className=' font-bold'>{emailOrigin.subject}</div>
<div>
<div className='flex justify-between'>
<div className='flex gap-2 mb-2 items-center'>

@ -1,10 +1,11 @@
import { createContext, useEffect, useState } from 'react';
import { Button, Tag, Radio, Popover, Form, Dropdown, Tabs, List, Image, Empty, Avatar } from 'antd';
import { FileSearchOutlined, FilterOutlined, FilterTwoTone } from '@ant-design/icons';
import { FilterIcon } from '@/components/Icons';
import { FilterIcon, InboxIcon, MailSendIcon, SendPlaneFillIcon, SendPlaneLineIcon } from '@/components/Icons';
import { isEmpty, objectMapper, stringToColour } from '@/utils/commons';
import useConversationStore from '@/stores/ConversationStore';
import { useShallow } from 'zustand/react/shallow';
import EmailDetail from './EmailDetail';
const CalColorStyle = (tag, outerStyle = true) => {
const color = stringToColour(tag);
@ -44,8 +45,8 @@ const MessageListFilter = ({ ...props }) => {
{data.length === 0 && <Empty description={false} />}
<div className='grid grid-cols-3 gap-2 max-h-96 overflow-y-auto'>
{data.map((item) => (
<video controls controlsList='nodownload nofullscreen noremoteplayback' key={item.data.id} className='max-h-20'>
<source src={item?.data.videoURL} type='video/mp4' />
<video controls controlsList='nodownload nofullscreen noremoteplayback' preload='metadata' key={item.data.id} className='max-h-24'>
<source src={`${item?.data.videoURL}#t=0.1`} type='video/mp4' />
Your browser does not support HTML video.
</video>
))}
@ -114,6 +115,14 @@ const MessageListFilter = ({ ...props }) => {
const EmailList = () => {
// todo:
const data = (activeMessages || []).filter((item) => item.type === 'email').reverse();
const [openEmailDetail, setOpenEmailDetail] = useState(false);
const [emailDetail, setEmailDetail] = useState({});
const onOpenEmail = (email_detail) => {
setOpenEmailDetail(true);
setEmailDetail(email_detail);
};
return (
<>
{/* {data.length === 0 && <Empty />} */}
@ -125,17 +134,30 @@ const MessageListFilter = ({ ...props }) => {
<List.Item actions={[item.localDate]}>
<List.Item.Meta
avatar={
<Avatar size='small' style={CalColorStyle(item.senderName)}>
{item.senderName.substring(0, 3)}
</Avatar>
item.sender === 'me' ? <SendPlaneFillIcon /> : <InboxIcon className='text-indigo-500' />
// <Avatar size='small' style={CalColorStyle(item.senderName)}>
// {item.senderName.substring(0, 3)}
// </Avatar>
}
title={
<Button
type='link'
onClick={() => {
onOpenEmail({ emailOrigin, ...item });
setOpenPopup(false);
}}
size='small'
className='px-0 font-bold'>
{emailOrigin.subject}
</Button>
}
title={emailOrigin.subject}
description={`To: ${emailOrigin.toEmail}`}
/>
{emailOrigin.abstract}
</List.Item>
)}
/>
<EmailDetail open={openEmailDetail} setOpen={setOpenEmailDetail} emailDetail={emailDetail} key={`email-detail-${emailDetail.id}`} />
</>
);
};

@ -1,5 +1,5 @@
import { createContext, useEffect, useState } from 'react';
import { Button, Form, Input, Flex, Checkbox, Switch, Mentions, Popover } from 'antd';
import { Button, Form, Input, Flex, Checkbox, Switch, Mentions, Popover, Popconfirm, Select } from 'antd';
import Modal from '@dckj/react-better-modal';
import '@dckj/react-better-modal/dist/index.css';
@ -27,6 +27,14 @@ import { isEmpty } from '@/utils/commons';
// function onError(error) {
// console.error(error);
// }
const getAbstract = (longtext) => {
const lines = longtext.split('\n');
const firstLine = lines[0];
const abstract = firstLine.substring(0, 20);
return abstract;
};
const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, initial = {}, action='reply', ...props }) => {
const [form] = Form.useForm();
@ -70,7 +78,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
setHtmlContent(html);
setTextContent(textContent);
form.setFieldValue('content', html);
form.setFieldValue('abstract', textContent.substring(0, 20));
form.setFieldValue('abstract', getAbstract(textContent));
};
const onHandleSend = () => {
@ -78,7 +86,8 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
// console.log(form.getFieldsValue());
const body = structuredClone(form.getFieldsValue());
body.content = isRichText ? htmlContent : textContent;
// console.log('body', body);
body.fromEmail = newFromEmail || fromEmail;
console.log('body', body);
form
.validateFields()
.then((values) => {
@ -92,6 +101,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
// setOpen(false);
};
const [newFromEmail, setNewFromEmail] = useState('');
const [initialForm, setInitialForm] = useState({});
const [initialContent, setInitialContent] = useState('');
useEffect(() => {
@ -149,6 +159,21 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
return () => {};
}, [quote, open]);
const [openPlainTextConfirm, setOpenPlainTextConfirm] = useState(false);
const handlePlainTextOpenChange = ({target}) => {
const {checked: newChecked} = target;
if (!newChecked) {
setIsRichText(true);
setOpenPlainTextConfirm(false);
return;
}
setOpenPlainTextConfirm(true);
};
const confirmPlainText = () => {
setIsRichText(false);
setOpenPlainTextConfirm(false);
}
return (
<>
<Modal
@ -177,16 +202,21 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
// onOk={onHandleOk}
onStageChange={onStageChange}
footer={
<div className='w-full flex gap-8 justify-start items-center text-indigo-600'>
<div className='w-full flex gap-6 justify-start items-center text-indigo-600'>
<Button type='primary' className='bg-indigo-500 shadow shadow-indigo-300 hover:!bg-indigo-400 active:bg-indigo-400 focus:bg-indigo-400' onClick={onHandleSend}>
发送
</Button>
<Popover>
{/* <Switch checkedChildren='纯文本' unCheckedChildren='HTML' /> */}
<Checkbox checked={!isRichText} onChange={(e) => setIsRichText(!e.target.checked)}>
纯文本
</Checkbox>
</Popover>
<Popconfirm
description='切换内容为纯文本格式将丢失信件核签名的格式, 确定使用纯文本?'
onConfirm={confirmPlainText}
open={openPlainTextConfirm}
>
<Checkbox checked={!isRichText} onChange={handlePlainTextOpenChange}>纯文本</Checkbox>
</Popconfirm>
<Select options={[
{key: 'lyt@hainatravel.com', value: 'lyt@hainatravel.com', label: 'LYT <lyt@hainatravel.com>' },
{key: 'ycc@chinahighlights.com', value: 'ycc@chinahighlights.com', label: 'YCC <ycc@chinahighlights.com>' },
]} value={newFromEmail || fromEmail} onChange={(val) => setNewFromEmail(val)} variant={'borderless'} />
</div>
}>
<Form

Loading…
Cancel
Save