diff --git a/src/actions/EmailActions.js b/src/actions/EmailActions.js index b0d3a01..df31896 100644 --- a/src/actions/EmailActions.js +++ b/src/actions/EmailActions.js @@ -14,6 +14,25 @@ export const parseHTMLString = (html, needText = false) => { return needText ? { html, bodyContent, bodyText } : bodyContent } +export const EMAIL_CHANNEL_NAME = 'mailbox_changes'; +let emailChangesChannel = null; +export function getEmailChangesChannel() { + if (!emailChangesChannel) { + emailChangesChannel = new BroadcastChannel(EMAIL_CHANNEL_NAME) + } + return emailChangesChannel +} + +// 通知邮件列表数据更新 +const notifyMailboxUpdate = (payload) => { + const notificationPayload = payload + // - 多个tab + const channel = getEmailChangesChannel() + channel.postMessage(notificationPayload) + // - 当前tab + internalEventEmitter.emit(EMAIL_CHANNEL_NAME, notificationPayload) +} + /** * 获取顾问签名 * @param {object} { opi_sn } @@ -178,7 +197,7 @@ export const getEmailDirAction = async (params = { opi_sn: '' }, retOrder=false) const orderList = groupBy(result, row => `${row.IsTrue}`)?.['0'] || []; return retOrder !== false ? orderList : { [`${params.opi_sn}`]: retTree } }; -export const getMailboxCountAction = async (params = { opi_sn: '' }, update = false) => { +export const getMailboxCountAction = async (params = { opi_sn: '' }, update = true) => { const defaultParams = { opi_sn: 0, // date1: dayjs().subtract(1, 'year').startOf('year').format(DATE_FORMAT), @@ -190,7 +209,7 @@ export const getMailboxCountAction = async (params = { opi_sn: '' }, update = fa // 更新数量 if (update !== false) { const readCacheDir = await readIndexDB(Number(params.opi_sn), 'dirs', 'mailbox'); - const mailboxDir = isEmpty(readCacheDir) ? [] : readCacheDir.tree.filter(node => node._raw.IsTrue === 1); + const mailboxDir = isEmpty(readCacheDir) ? [] : readCacheDir.tree.filter(node => node?._raw?.IsTrue === 1); const _MapDir = new Map(mailboxDir.map((obj) => [obj.key, obj])) Object.keys(result).map(dirKey => { _MapDir.set(Number(dirKey), {..._MapDir.get(Number(dirKey)), count: result[dirKey]}); @@ -202,6 +221,7 @@ export const getMailboxCountAction = async (params = { opi_sn: '' }, update = fa }) const _newRoot = Array.from(_MapRoot.values()) writeIndexDB([{ key: Number(params.opi_sn), tree: _newRoot }], 'dirs', 'mailbox') + notifyMailboxUpdate({ type: 'dirs', key: Number(params.opi_sn) }) } return ret; @@ -315,7 +335,7 @@ export const getRootMailboxDirAction = async ({ opi_sn = 0, userIdStr = '' } = { getTodoOrdersAction({ opisn: userIdStr || String(opi_sn), otype: 'today' }), ...(userIdStr.split(',').map(_opi => getEmailDirAction({ opi_sn: _opi }))), ]) - const mailBoxCount = await Promise.all(userIdStr.split(',').map(_opi => getMailboxCountAction({ opi_sn: _opi }))); + const mailBoxCount = await Promise.all(userIdStr.split(',').map(_opi => getMailboxCountAction({ opi_sn: _opi }, false))); const mailboxDirCountByOPI = mailBoxCount.reduce((a, c) => ({ ...a, ...c, }), {}) const mailboxDirByOPI = mailboxDir.reduce((a, c) => ({ ...a, ...(Object.keys(c).reduce((a, opi) => ({...a, [opi]: c[`${opi}`].map((dir) => ({ ...dir, count: mailboxDirCountByOPI[opi][`${dir.key}`] })) }), {} )) }), {}) const rootTree = Object.keys(stickyTree).map((opi) => ({ key: Number(opi), tree: [...stickyTree[opi], ...(mailboxDirByOPI?.[opi] || [])] })) @@ -355,15 +375,16 @@ export const queryEmailListAction = async ({ opi_sn = '', pagesize = 10, last_id return ret; } -export const EMAIL_CHANNEL_NAME = 'mailbox_changes'; -let emailChangesChannel = null; -export function getEmailChangesChannel() { - if (!emailChangesChannel) { - emailChangesChannel = new BroadcastChannel(EMAIL_CHANNEL_NAME) +const removeFromCurrentList = async (params) => { + const readRow0 = await readIndexDB(params.mai_sn_list[0], 'listrow', 'mailbox') + const listKey = readRow0?.data?.listKey || '' + if (listKey) { + const readCache = await readIndexDB(listKey, 'maillist', 'mailbox') + const updatedMailList = readCache.data.filter((mai_sn) => !params.mai_sn_list.includes(mai_sn)) + writeIndexDB([{ key: listKey, data: updatedMailList }], 'maillist', 'mailbox') + notifyMailboxUpdate({ type: 'listrow', listKey, affectKeys: params.mai_sn_list }) } - return emailChangesChannel } - const updateEmailKeyMap = { read: 'MOI_ReadState' }; const updateEmailKeyFun = { read: async (params) => { @@ -374,31 +395,13 @@ const updateEmailKeyFun = { 'listrow', 'mailbox', ) - const listKey = readCache.get(params.mai_sn_list[0])?.data?.listKey || ''; // 通知邮件列表数据更新 + const listKey = readCache.get(params.mai_sn_list[0])?.data?.listKey || ''; const notificationPayload = { type: 'listrow', listKey, affectKeys: params.mai_sn_list } - // - 多个tab - const channel = getEmailChangesChannel() - channel.postMessage(notificationPayload) - // - 当前tab - internalEventEmitter.emit(EMAIL_CHANNEL_NAME, notificationPayload); + notifyMailboxUpdate(notificationPayload) }, - processed: async (params) => { - const readRow0 = await readIndexDB(params.mai_sn_list[0], 'listrow', 'mailbox') - const listKey = readRow0?.data?.listKey || ''; - if (listKey) { - const readCache = await readIndexDB(listKey, 'maillist', 'mailbox') - const updatedMailList = readCache.data.filter(mai_sn => !params.mai_sn_list.includes(mai_sn)); - writeIndexDB([{ key: listKey, data: updatedMailList }], 'maillist', 'mailbox') - // 通知邮件列表数据更新 - const notificationPayload = { type: 'listrow', listKey, affectKeys: params.mai_sn_list } - // - 多个tab - const channel = getEmailChangesChannel() - channel.postMessage(notificationPayload) - // - 当前tab - internalEventEmitter.emit(EMAIL_CHANNEL_NAME, notificationPayload); - } - } + processed: removeFromCurrentList, + delete: removeFromCurrentList } /** * 更新邮件属性 @@ -414,6 +417,7 @@ export const updateEmailAction = async (params = { opi_sn: 0, mai_sn_list: [], s updateFun(params) } } + getMailboxCountAction({ opi_sn: params.opi_sn }); return errcode === 0 ? result : {} } diff --git a/src/hooks/useEmail.js b/src/hooks/useEmail.js index 4eec062..5833a3c 100644 --- a/src/hooks/useEmail.js +++ b/src/hooks/useEmail.js @@ -201,6 +201,17 @@ export const useEmailList = (mailboxDirNode) => { [VKey], ) + const markAsDeleted = useCallback( + async (sn_list) => { + await updateEmailAction({ + opi_sn: opi_sn, + mai_sn_list: sn_list, + set: { delete: 1 }, + }) + }, + [VKey], + ) + const loadMailListFromCache = useCallback(async (payload) => { const cacheKey = isEmpty(COLI_SN) ? `dir-${VKey}` : `order-${VKey}` const readCacheIDList = await readIndexDB(cacheKey, 'maillist', 'mailbox') @@ -276,7 +287,7 @@ export const useEmailList = (mailboxDirNode) => { } }, [getMailList]) - return { loading, isFreshData, error, mailList, refresh, markAsRead, markAsProcessed } + return { loading, isFreshData, error, mailList, refresh, markAsRead, markAsProcessed, markAsDeleted } } const orderMailTypes = new Map([ diff --git a/src/stores/EmailSlice.js b/src/stores/EmailSlice.js index 649683b..a3b3ca1 100644 --- a/src/stores/EmailSlice.js +++ b/src/stores/EmailSlice.js @@ -1,6 +1,7 @@ -import { getEmailDirAction, getMailboxCountAction, getRootMailboxDirAction } from '@/actions/EmailActions' +import { getEmailDirAction, getMailboxCountAction, getRootMailboxDirAction, getEmailChangesChannel, EMAIL_CHANNEL_NAME } from '@/actions/EmailActions' import { buildTree, isEmpty, olog, sortArrayByOrder } from '@/utils/commons' import { readIndexDB, writeIndexDB, createIndexedDBStore, clean7DaysMailboxLog } from '@/utils/indexedDB'; +import { internalEventEmitter } from '@/utils/EventEmitterService'; /** * Email @@ -141,20 +142,43 @@ const emailSlice = (set, get) => ({ // 更新数量 updateMailboxCount: async ({ opi_sn }) => { const { setMailboxNestedDirsActive } = get() - await getMailboxCountAction({ opi_sn }, true) - const readCache = await readIndexDB(Number(opi_sn), 'dirs', 'mailbox') - if (!isEmpty(readCache)) { - setMailboxNestedDirsActive(readCache?.tree || []) - } + await getMailboxCountAction({ opi_sn }) + // const readCache = await readIndexDB(Number(opi_sn), 'dirs', 'mailbox') + // if (!isEmpty(readCache)) { + // setMailboxNestedDirsActive(readCache?.tree || []) + // } }, async initMailbox({ opi_sn, dei_sn, userIdStr }) { olog('initMailbox ---- ') - const { setCurrentMailboxOPI, setCurrentMailboxDEI, getOPIEmailDir } = get() + const { currentMailboxOPI, setCurrentMailboxOPI, setCurrentMailboxDEI, getOPIEmailDir, setMailboxNestedDirsActive, } = get() createIndexedDBStore(['dirs', 'maillist', 'listrow', 'mailinfo', 'draft'], 'mailbox') setCurrentMailboxOPI(opi_sn) setCurrentMailboxDEI(dei_sn) getOPIEmailDir(opi_sn, userIdStr, true) + + // --- Setup Internal Event Listener --- + internalEventEmitter.on(EMAIL_CHANNEL_NAME, async (event) => { + console.log(`🔔Received internal event. `, event.detail) + if (event.detail && event.detail.type === 'dirs') { + const readCache = await readIndexDB(event.detail.key, 'dirs', 'mailbox') + if (!isEmpty(readCache)) { + setMailboxNestedDirsActive(readCache?.tree || []) + } + } + }) + // --- Setup BroadcastChannel Listener --- + const channel = getEmailChangesChannel() + channel.addEventListener('message', async (event) => { + console.log(`📣Received channel event. `, event.data) + if (event.data.type === 'dirs' && currentMailboxOPI === event.data.key) { + const readCache = await readIndexDB(event.data.key, 'dirs', 'mailbox') + if (!isEmpty(readCache)) { + setMailboxNestedDirsActive(readCache?.tree || []) + } + } + }) + }, }) export default emailSlice diff --git a/src/views/NewEmail.jsx b/src/views/NewEmail.jsx index 055dcc7..af3bae2 100644 --- a/src/views/NewEmail.jsx +++ b/src/views/NewEmail.jsx @@ -245,7 +245,7 @@ const NewEmail = () => { mai_sn: pageParam.quoteid, ..._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 case 'new': diff --git a/src/views/orders/components/MailBox.jsx b/src/views/orders/components/MailBox.jsx index a820ec0..1fe6234 100644 --- a/src/views/orders/components/MailBox.jsx +++ b/src/views/orders/components/MailBox.jsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' -import { ReloadOutlined, ReadOutlined, RightOutlined, LeftOutlined, SearchOutlined, MailOutlined } from '@ant-design/icons' -import { Flex, Button, Tooltip, List, Form, Row, Col, Input, Checkbox, DatePicker, Switch, Breadcrumb, Skeleton } from 'antd' +import { ReloadOutlined, ReadOutlined, RightOutlined, LeftOutlined, SearchOutlined, MailOutlined, DeleteOutlined } from '@ant-design/icons' +import { Flex, Button, Tooltip, List, Form, Row, Col, Input, Checkbox, DatePicker, Switch, Breadcrumb, Skeleton, Popconfirm } from 'antd' import dayjs from 'dayjs' import { useEmailList } from '@/hooks/useEmail' import { isEmpty } from '@/utils/commons' @@ -42,7 +42,7 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => { ] const [form] = Form.useForm() const [selectedItems, setSelectedItems] = useState([]) - const { mailList, loading, error, refresh, markAsRead, markAsProcessed } = useEmailList(mailboxDir) + const { mailList, loading, error, refresh, markAsRead, markAsProcessed, markAsDeleted } = useEmailList(mailboxDir) const [pagination, setPagination] = useState({ current: 1, @@ -97,6 +97,8 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => { const mailItemRender = (item) => { const isOrderNode = mailboxDir.COLI_SN > 0 const orderNumber = isEmpty(item.MAI_COLI_ID) || isOrderNode ? '' : item.MAI_COLI_ID + ' - ' + const folderName = isOrderNode ? `[${item.FDir}]` : '' + const orderMailType = {item.MAT_Name} const countryName = isEmpty(item.CountryCN) ? '' : '[' + item.CountryCN + '] ' const mailStateClass = item.MOI_ReadState === 0 ? 'font-bold' : '' const hasAtta = item.MAI_Attachment !== 0 ? : null @@ -173,6 +175,13 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => { markAsProcessed(selectedItems.map((item) => item.MAI_SN)).then(() => setSelectedItems([])) }} >已处理 +