diff --git a/src/actions/EmailActions.js b/src/actions/EmailActions.js index 19eb2de..427e107 100644 --- a/src/actions/EmailActions.js +++ b/src/actions/EmailActions.js @@ -165,7 +165,8 @@ const todoTypes = { } /** * 顾问的邮箱目录 - * @param {object} { opi_sn, year, by_start_date, by_success, important, if_want_book, if_thinking } + * @param {object} params { opi_sn, year, by_start_date, by_success, important, if_want_book, if_thinking } + * @param {boolean} retOrder 是否直接返回订单列表 -- 忽略 */ export const getEmailDirAction = async (params = { opi_sn: '' }, retOrder=false) => { const defaultParams = { opi_sn: 0, year: dayjs().year(), by_start_date: -1, by_success: -1, important: -1, if_want_book: -1, if_thinking: -1 } @@ -177,6 +178,35 @@ 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) => { + const defaultParams = { + opi_sn: 0, + // date1: dayjs().subtract(1, 'year').startOf('year').format(DATE_FORMAT), + date1: dayjs().subtract(180, 'days').format(DATE_FORMAT), + date2: dayjs().format(DATEEND_FORMAT) + } + const { errcode, result } = await fetchJSON(`${API_HOST_V3}/dir_count`, {...defaultParams, ...params}) + const ret = errcode !== 0 ? { [`${params.opi_sn}`]: {} } : { [`${params.opi_sn}`]: result } + // 更新数量 + 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 _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]}); + }) + console.log(_MapDir, '_MapDir') + const _newToUpdate = Array.from(_MapDir.values()); + const _MapRoot = new Map((readCacheDir?.tree || []).map((obj) => [obj.key, obj])) + _newToUpdate.forEach((row) => { + _MapRoot.set(row.key, row) + }) + const _newRoot = Array.from(_MapRoot.values()) + writeIndexDB([{ key: Number(params.opi_sn), tree: _newRoot }], 'dirs', 'mailbox') + } + + return ret; +}; export const getTodoOrdersAction = async (params) => { const opi_arr = params.opisn.split(',') const defaultStickyTree = opi_arr.reduce( @@ -211,7 +241,7 @@ export const getTodoOrdersAction = async (params) => { {}, ) const { errcode, result } = await fetchJSON(`${API_HOST}/getwlorder`, params) - // 取后一个状态, 因此翻转两次 + // 订单重复时, 取后一个状态, 因此翻转两次 const _result_unique = uniqWith(result.reverse(), (a, b) => a.COLI_SN === b.COLI_SN).reverse(); const orderList = errcode === 0 ? _result_unique : [] const byOPI = groupBy(orderList, 'OPI_SN') @@ -286,7 +316,9 @@ 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 mailboxDirByOPI = mailboxDir.reduce((a, c) => ({...a, ...c}), {}); + const mailBoxCount = await Promise.all(userIdStr.split(',').map(_opi => getMailboxCountAction({ opi_sn: _opi }))); + 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] || [])] })) writeIndexDB(rootTree, 'dirs', 'mailbox') const _mapped = groupBy(rootTree, 'key') @@ -304,7 +336,8 @@ export const queryEmailListAction = async ({ opi_sn = '', pagesize = 10, last_id vkey: 0, vparent: 0, order_source_type: 0, - mai_senddate1: dayjs().subtract(1, 'year').startOf('year').format(DATE_FORMAT), + // mai_senddate1: dayjs().subtract(1, 'year').startOf('year').format(DATE_FORMAT), + mai_senddate1: dayjs().subtract(180, 'days').format(DATE_FORMAT), mai_senddate2: dayjs().format(DATEEND_FORMAT), ...omitEmpty({ ...node, diff --git a/src/hooks/useEmail.js b/src/hooks/useEmail.js index 114815a..4eec062 100644 --- a/src/hooks/useEmail.js +++ b/src/hooks/useEmail.js @@ -54,7 +54,7 @@ export const useEmailDetail = (mai_sn=0, data={}, oid=0, markRead=false) => { const refresh = useCallback(() => { setRefreshTrigger(prev => prev + 1); }, []); - console.log(maiSN, 'mailSN', mai_sn) + // console.log(maiSN, 'mailSN', mai_sn) // const [updateMessageItem] = useConversationStore(state => [state.updateMessageItem]); diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index b3c747e..83d9974 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -179,7 +179,7 @@ const websocketSlice = (set, get) => ({ logWebsocket(data, 'I'); // olog('websocket Messages ----', data); // console.log(data); - const { updateMessageItem, sentOrReceivedNewMessage, addGlobalNotify, setWai, addToConversationList } = get(); + const { updateMessageItem, sentOrReceivedNewMessage, addGlobalNotify, setWai, addToConversationList, updateMailboxCount } = get() const { errcode, errmsg, result } = data; if (!result) { @@ -195,6 +195,14 @@ const websocketSlice = (set, get) => ({ const msgRender = receivedMsgTypeMapped[resultType].contentToRender(msgObj); const msgUpdate = receivedMsgTypeMapped[resultType].contentToUpdate(msgObj); // console.log('msgRender msgUpdate', msgRender, msgUpdate); + if (['email.updated', 'email.inbound.received',].includes(resultType)) { + updateMailboxCount({ opi_sn: msgObj.opi_sn }) + if (!isEmpty(msgRender)) { + const msgNotify = receivedMsgTypeMapped[resultType].contentToNotify(msgObj); + addGlobalNotify(msgNotify); + } + return false; + } if ([ 'whatsapp.message.updated', 'message', 'error', 'email.updated', 'wai.message.updated', diff --git a/src/stores/EmailSlice.js b/src/stores/EmailSlice.js index 44a46d6..649683b 100644 --- a/src/stores/EmailSlice.js +++ b/src/stores/EmailSlice.js @@ -1,5 +1,5 @@ -import { getEmailDirAction, getRootMailboxDirAction } from '@/actions/EmailActions' -import { buildTree, isEmpty, sortArrayByOrder } from '@/utils/commons' +import { getEmailDirAction, getMailboxCountAction, getRootMailboxDirAction } from '@/actions/EmailActions' +import { buildTree, isEmpty, olog, sortArrayByOrder } from '@/utils/commons' import { readIndexDB, writeIndexDB, createIndexedDBStore, clean7DaysMailboxLog } from '@/utils/indexedDB'; /** @@ -85,18 +85,14 @@ const emailSlice = (set, get) => ({ setMailboxNestedDirsActive: (dir) => { return set(() => ({ mailboxNestedDirsActive: dir })) }, - updateMailboxNestedDirs: (dirs) => { - const { mailboxNestedDirsActive } = get(); - const keep = mailboxNestedDirsActive.filter((ele) => isEmpty(ele.parent)) - return set(() => ({ mailboxNestedDirsActive: [...keep, ...dirs] })) - }, - addMailboxNestedDirs: dirs => { + updateCurrentMailboxNestedDirs: (dirs) => { const { mailboxNestedDirsActive } = get() const _Map = new Map(mailboxNestedDirsActive.map((obj) => [obj.key, obj])) dirs.forEach((row) => { _Map.set(row.key, row) }) - const _newValue = sortArrayByOrder(Array.from(_Map.values()), 'key', ['search-orders']) + // const _newValue = sortArrayByOrder(Array.from(_Map.values()), 'key', ['search-orders']) + const _newValue = Array.from(_Map.values()) return set(() => ({ mailboxNestedDirsActive: _newValue })) }, @@ -119,33 +115,46 @@ const emailSlice = (set, get) => ({ return set(() => ({ mailboxActiveCOLI: coli })) }, - getOPIEmailDir: async (opi_sn = 0, userIdStr='') => { + getOPIEmailDir: async (opi_sn = 0, userIdStr = '', refreshNow = false) => { // console.log('🌐requesting opi dir', opi_sn, typeof opi_sn) - const { setMailboxNestedDirsActive } = get() + const { setMailboxNestedDirsActive, updateMailboxCount } = get() const readCache = await readIndexDB(Number(opi_sn), 'dirs', 'mailbox') // console.log(readCache); - let isNeedRefresh = false + let isNeedRefresh = refreshNow if (!isEmpty(readCache)) { setMailboxNestedDirsActive(readCache?.tree || []) - isNeedRefresh = Date.now() - readCache.timestamp > 1 * 60 * 60 * 1000 + isNeedRefresh = refreshNow || Date.now() - readCache.timestamp > 1 * 60 * 60 * 1000 // isNeedRefresh = true; // test: 0 } if (isEmpty(readCache) || isNeedRefresh) { // > {4} 更新 - const rootTree = await getRootMailboxDirAction({ opi_sn, userIdStr }) + const rootTree = await getRootMailboxDirAction({ opi_sn, userIdStr: String(userIdStr || opi_sn) }) // console.log('empty', opi_sn, userIdStr, isEmpty(readCache), isNeedRefresh, rootTree); setMailboxNestedDirsActive(rootTree) + } else { + // 只更新数量 + updateMailboxCount({ opi_sn }) } return false }, - async initMailbox({ opi_sn, dei_sn, userId, userIdStr }) { + // 更新数量 + 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 || []) + } + }, + + async initMailbox({ opi_sn, dei_sn, userIdStr }) { + olog('initMailbox ---- ') const { setCurrentMailboxOPI, setCurrentMailboxDEI, getOPIEmailDir } = get() createIndexedDBStore(['dirs', 'maillist', 'listrow', 'mailinfo', 'draft'], 'mailbox') setCurrentMailboxOPI(opi_sn) setCurrentMailboxDEI(dei_sn) - getOPIEmailDir(opi_sn, userIdStr) + getOPIEmailDir(opi_sn, userIdStr, true) }, - }) export default emailSlice diff --git a/src/views/orders/Follow.jsx b/src/views/orders/Follow.jsx index 32e19da..ed60ba5 100644 --- a/src/views/orders/Follow.jsx +++ b/src/views/orders/Follow.jsx @@ -1,7 +1,7 @@ import useAuthStore from '@/stores/AuthStore' import { pick } from '@/utils/commons' import { UnorderedListOutlined, LeftOutlined } from '@ant-design/icons' -import { Flex, Segmented, Tree, Typography, Layout, Splitter, Button, Tooltip } from 'antd' +import { Flex, Segmented, Tree, Typography, Layout, Splitter, Button, Tooltip, Badge } from 'antd' import { useEffect, useMemo, useState } from 'react' import EmailDetailInline from '../Conversations/Online/Components/EmailDetailInline' import OrderProfile from '@/components/OrderProfile' @@ -134,11 +134,13 @@ function Follow() { - } - titleRender={(node) => {node.title}} + titleRender={(node) => ( + + {node.title} + + + )} /> @@ -163,9 +170,14 @@ function Follow() { - - : } onClick={() => setCollapsed(!collapsed)} className={`absolute z-10 rounded-none ${collapsed ? 'right-1 top-36 rounded-l-xl' : 'right-8 top-20 rounded-l'}`} size={collapsed ? 'small' : 'middle'} /> - + + : } + onClick={() => setCollapsed(!collapsed)} + className={`absolute z-10 rounded-none ${collapsed ? 'right-1 top-36 rounded-l-xl' : 'right-8 top-20 rounded-l'}`} + size={collapsed ? 'small' : 'middle'} + /> + { const [currentMailboxOPI] = useConversationStore((state) => [state.currentMailboxOPI]) - const [addMailboxNestedDirs, updateMailboxNestedDirs, setMailboxActiveNode] = useConversationStore((state) => [state.addMailboxNestedDirs, state.updateMailboxNestedDirs, state.setMailboxActiveNode]) + const [updateCurrentMailboxNestedDirs, setMailboxActiveNode] = useConversationStore((state) => [state.updateCurrentMailboxNestedDirs, state.setMailboxActiveNode]) const [open, setOpen] = useState(false) const [form] = Form.useForm() @@ -31,7 +31,7 @@ const MailOrderSearchModal = ({ ...props }) => { if (isEmpty(valuesToSub.coli_id)) { const { coli_id, sourcetype, ...mailboxParams} = valuesToSub; result = await getEmailDirAction({ ...mailboxParams, opi_sn: currentMailboxOPI }, false) - updateMailboxNestedDirs(result[`${currentMailboxOPI}`]) + updateCurrentMailboxNestedDirs(result[`${currentMailboxOPI}`]) } else { const htOrderParams = pick(valuesToSub, ['coli_id', 'sourcetype',]) result = await queryHTOrderListAction({...htOrderParams, opi_sn: currentMailboxOPI}) @@ -42,7 +42,7 @@ const MailOrderSearchModal = ({ ...props }) => { _raw: { COLI_SN: 0, IsTrue: 0 }, children: result.map((o) => ({ key: `search-${o.COLI_SN}`, title: `${o.COLI_ID}`, iconIndex: 13, parent: 'search-orders', parentTitle: '搜索结果', parentIconIndex: 'search', _raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent: 'search-orders', IsTrue: 0, ApplyDate: '', OrderSourceType: htOrderParams.sourcetype, parent: 'search-orders' }, })), } - addMailboxNestedDirs([addToTree]); + updateCurrentMailboxNestedDirs([addToTree]); setMailboxActiveNode(addToTree); } setLoading(false)