perf: 待办目录+邮箱目录

dev/ckeditor
Lei OT 4 months ago
parent a61ed7eb82
commit c7d72d01f3

@ -1,6 +1,6 @@
import { fetchJSON, postForm, postJSON } from '@/utils/request';
import { API_HOST, DATE_FORMAT, DATEEND_FORMAT, DATETIME_FORMAT, EMAIL_HOST } from '@/config';
import { buildTree, isEmpty, objectMapper, omitEmpty, readIndexDB, uniqWith, writeIndexDB } from '@/utils/commons';
import { buildTree, groupBy, isEmpty, objectMapper, omitEmpty, readIndexDB, uniqWith, writeIndexDB } from '@/utils/commons';
import dayjs from 'dayjs';
const parseHTMLString = (html, needText = false) => {
@ -146,6 +146,18 @@ export const fetchEmailBindOrderAction = async (params) => {
return errcode === 0 ? true : false;
}
const todoTypes = {
// 1新订单2未读消息3需一催4需二催5需三催6未处理邮件入境提醒coli_ordertype=7余款提醒coli_ordertype=8
1: '新订单',
2: '未读',
3: '一催',
4: '二催',
5: '三催',
6: '未处理',
7: '入境提醒',
8: '余款提醒',
}
/**
* 顾问的邮箱目录
* @param {object} { opi_sn }
@ -153,8 +165,113 @@ export const fetchEmailBindOrderAction = async (params) => {
export const getEmailDirAction = async (params = { opi_sn: '' }) => {
const defaultParams = { opi_sn: 0, year: dayjs().year(), by_start_date: -1, by_success: -1, important: -1, if_want_book: -1, if_thinking: -1 }
const { errcode, result } = await fetchJSON(`http://202.103.68.144:8889/v3/email_dir`, { ...defaultParams, ...params })
return errcode === 0 ? result : [];
} ;
const mailboxSort = result //.sort(sortBy('MDR_Order'));
let tree = buildTree(mailboxSort, { key: 'VKey', parent: 'VParent', name: 'VName', iconIndex: 'ImageIndex', rootKeys: [1], ignoreKeys: [-227001, -227002] })
tree = tree.filter((ele) => ele.key !== 1)
return errcode === 0 ? tree : [];
};
export const getTodoOrdersAction = async (params) => {
const opi_arr = params.opisn.split(',');
const defaultStickyTree = opi_arr.reduce(
(a, ele) => ({
...a,
[ele]: [
{
key: ele + '-today',
title: '今日任务',
getMails: false,
iconIndex: 'star',
children: [],
COLI_SN: 0,
},
{
key: ele + '-todo',
title: '待办任务',
getMails: false,
iconIndex: 'calendar',
children: [],
COLI_SN: 0,
},
// {
// key: ele.OPI_DEI_SN + '-reminder',
// title: '催信',
// getMails: false,iconIndex: 'reminder',
// icon: <BellTwoTone />,
// children: [], COLI_SN: 0,
// },
],
}),
{},
)
const { errcode, result } = await fetchJSON(`${API_HOST}/getwlorder`, params)
const orderList = errcode === 0 ? result : [];
const byOPI = groupBy(orderList, 'OPI_SN');
const byState = Object.keys(byOPI).reduce((acc, key) => {
const sticky = groupBy(byOPI[key], (ele) => ([1, 2, 6].includes(ele.coli_ordertype) ? 0 : ([3,4,5].includes(ele.coli_ordertype) ? 1 : 2)))
const treeNode = [
{
key: key + '-today',
title: '今日任务',
getMails: false,iconIndex: 'star',
_raw: {COLI_SN: 0, IsTrue: 0,},
children: (sticky[0] || []).map((o) => ({
key: `today-${o.COLI_SN}`,
title: `(${todoTypes[o.coli_ordertype] || o.COLI_State}) ${o.COLI_ID}`,
iconIndex: 13,
parent: key + '-today',
parentTitle: '今日任务',
parentIconIndex: 'star',
_raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent:-1, IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: -1 },
})),
},
{
key: key + '-todo',
title: '待办任务',
getMails: false,iconIndex: 'calendar',
_raw: {COLI_SN: 0, IsTrue: 0,},
children: (sticky[2] || []).map((o) => ({
key: `todo-${o.COLI_SN}`,
title: `(${todoTypes[o.coli_ordertype] || o.COLI_State}) ${o.COLI_ID}`,
iconIndex: 13,
parent: key + '-todo',
parentTitle: '待办任务',
parentIconIndex: 'calendar',
_raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent:-1, IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: -1 },
})),
},
{
key: key + '-reminder',
title: '催信',
getMails: false,iconIndex: 'reminder',
_raw: {COLI_SN: 0, IsTrue: 0,},
children: (sticky[1] || []).map((o) => ({
key: `reminder-${o.COLI_SN}`,
title: `(${todoTypes[o.coli_ordertype] || o.COLI_State}) ${o.COLI_ID}`,
iconIndex: 13,
parent: key + '-reminder',
parentTitle: '催信',
parentIconIndex: 'reminder',
_raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent:-1, IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: -1 },
})),
},
]
return { ...acc, [key]: treeNode }
}, defaultStickyTree)
return errcode === 0 ? byState : defaultStickyTree;
};
/**
* 获取待办目录和邮箱目录
* @param {object} params { opi_sn, userIdStr }
* @param {number} params.opi_sn
* @param {string} params.userIdStr - 用户ID字符串默认为空
*/
export const getRootMailboxDirAction = async ({ opi_sn = 0, userIdStr = '' } = {}) => {
const [stickyTree, mailboxDir] = await Promise.all([getTodoOrdersAction({ opisn: userIdStr || String(opi_sn), otype: 'today' }), getEmailDirAction({ opi_sn: opi_sn })])
const rootTree = Object.keys(stickyTree).map((opi) => ({ key: Number(opi), tree: [...stickyTree[opi], ...mailboxDir] }))
writeIndexDB(rootTree, 'dirs', 'mailbox')
const _mapped = groupBy(rootTree, 'key')
return _mapped[opi_sn]?.[0]?.tree || []
}
/**
* 获取邮件列表

@ -577,7 +577,7 @@ export const useConversationStore = create(
const { addToConversationList, setTemplates, setInitial, setTags,
initMailbox } = get();
initMailbox({ userId, dei_sn: loginUser.accountList[0].OPI_DEI_SN, opi_sn: loginUser.accountList[0].OPI_SN })
initMailbox({ userId, dei_sn: loginUser.accountList[0].OPI_DEI_SN, opi_sn: loginUser.accountList[0].OPI_SN, userIdStr: loginUser.userIdStr })
const conversationsList = await fetchConversationsList({ opisn: userId });
addToConversationList(conversationsList);

@ -1,4 +1,4 @@
import { getEmailDirAction } from '@/actions/EmailActions'
import { getEmailDirAction, getRootMailboxDirAction } from '@/actions/EmailActions'
import { buildTree, isEmpty, readIndexDB, writeIndexDB, createIndexedDBStore } from '@/utils/commons'
/**
@ -99,36 +99,33 @@ const emailSlice = (set, get) => ({
return set(() => ({ mailboxActiveMAI: mai }))
},
getOPIEmailDir: async (opi_sn = 0) => {
getOPIEmailDir: async (opi_sn = 0, userIdStr='') => {
// console.log('🌐requesting opi dir', opi_sn, typeof opi_sn)
const { setMailboxNestedDirsActive } = get()
const readCache = await readIndexDB(Number(opi_sn), 'dirs', 'mailbox')
// console.log(readCache);
// setMailboxNestedDirs(Number(opi_sn), readCache.tree)
let isNeedRefresh = false
if (!isEmpty(readCache)) {
setMailboxNestedDirsActive(readCache?.tree || [])
isNeedRefresh = true; // Date.now() - readCache.timestamp > 4 * 60 * 60 * 1000 // test: 0
isNeedRefresh = Date.now() - readCache.timestamp > 4 * 60 * 60 * 1000
isNeedRefresh = true; // test: 0
}
if (isEmpty(readCache) || isNeedRefresh) {
// > {4} 更新
const x = await getEmailDirAction({ opi_sn })
const mailboxSort = x //.sort(sortBy('MDR_Order'));
let tree = buildTree(mailboxSort, { key: 'VKey', parent: 'VParent', name: 'VName', iconIndex: 'ImageIndex', rootKeys: [1], ignoreKeys: [-227001, -227002] })
tree = tree.filter((ele) => ele.key !== 1)
writeIndexDB([{ key: Number(opi_sn), tree }], 'dirs', 'mailbox')
// setMailboxNestedDirs(Number(opi_sn), tree)
setMailboxNestedDirsActive(tree)
const rootTree = await getRootMailboxDirAction({ opi_sn, userIdStr })
setMailboxNestedDirsActive(rootTree)
}
return false
},
async initMailbox({ opi_sn, dei_sn, userId }) {
async initMailbox({ opi_sn, dei_sn, userId, userIdStr }) {
const { setCurrentMailboxOPI, setCurrentMailboxDEI, getOPIEmailDir } = get()
createIndexedDBStore(['dirs', 'maillist', 'mailinfo'], 'mailbox')
setCurrentMailboxOPI(opi_sn)
setCurrentMailboxDEI(dei_sn)
getOPIEmailDir(userId)
getOPIEmailDir(userId, userIdStr)
// 登录后, 执行一下清除缓存
// clean7DaysMailboxLog();
},
})

@ -1,33 +1,14 @@
import useAuthStore from '@/stores/AuthStore'
import useFormStore from '@/stores/FormStore'
import { useOrderStore } from '@/stores/OrderStore'
import { isEmpty, groupBy, buildTree, readIndexDB, writeIndexDB, pick } from '@/utils/commons'
import { StarTwoTone, CalendarTwoTone, FolderOutlined, DeleteOutlined, ClockCircleOutlined, FormOutlined, DatabaseOutlined, UnorderedListOutlined, LeftOutlined } from '@ant-design/icons'
import { Flex, Drawer, Radio, Divider, Segmented, Tree, Typography, Checkbox, Layout, Row, Col, Empty, Splitter, Button } from 'antd'
import { pick } from '@/utils/commons'
import { UnorderedListOutlined, LeftOutlined } from '@ant-design/icons'
import { Flex, Segmented, Tree, Typography, Layout, Splitter, Button } from 'antd'
import { useEffect, useMemo, useState } from 'react'
import { InboxIcon, MailUnreadIcon, SendPlaneFillIcon } from '@/components/Icons'
import { useShallow } from 'zustand/react/shallow'
import EmailDetailInline from '../Conversations/Online/Components/EmailDetailInline'
import { getEmailDirAction, queryEmailListAction } from '@/actions/EmailActions'
import OrderProfile from '@/components/OrderProfile'
import Mailbox from './components/Mailbox'
import useConversationStore from '@/stores/ConversationStore';
import dayjs from 'dayjs'
import { DATE_FORMAT, DATETIME_FORMAT } from '@/config'
import { MailboxDirIcon } from './components/MailboxDirIcon'
const todoTypes = {
// 123456coli_ordertype=7coli_ordertype=8
1: '新订单',
2: '未读',
3: '一催',
4: '二催',
5: '三催',
6: '未处理',
7: '入境提醒',
8: '余款提醒',
}
const deptMap = new Map([
['1', 'CH'], // CH
['2', 'CH大客户组'],
@ -62,8 +43,6 @@ const deptMap = new Map([
])
function Follow() {
const orderList = useOrderStore((state) => state.orderList)
const fetchOrderList = useOrderStore((state) => state.fetchOrderList)
const [collapsed, setCollapsed] = useState(false)
@ -73,30 +52,6 @@ function Follow() {
const accountDEI = useMemo(() => {
return accountList.map((ele) => ({ key: ele.OPI_DEI_SN, value: ele.OPI_DEI_SN, label: deptMap.get(`${ele.OPI_DEI_SN}`) }))
}, [accountList])
const defaultStickyTree = useMemo(() => {
return accountList.reduce(
(a, ele) => ({
...a,
[ele.OPI_DEI_SN]: [
{
title: '今日任务',
key: ele.OPI_DEI_SN + '-today',
getMails: false, iconIndex: 'star',
icon: <StarTwoTone />,
children: [], COLI_SN: 0,
},
{
title: '待办任务',
key: ele.OPI_DEI_SN + '-todo',
getMails: false, iconIndex: 'calendar',
icon: <CalendarTwoTone />,
children: [], COLI_SN: 0,
},
],
}),
{},
)
}, [accountList])
const [getOPIEmailDir] = useConversationStore(state => [state.getOPIEmailDir]);
const [currentMailboxDEI, setCurrentMailboxDEI, mailboxNestedDirsActive] = useConversationStore(state => [state.currentMailboxDEI, state.setCurrentMailboxDEI, state.mailboxNestedDirsActive]);
@ -113,18 +68,12 @@ function Follow() {
return x
}, [mailboxActiveNode.VKey])
// const [activeEmailId, setActiveEmailId] = useState(0)
const [deiStickyTree, setDeiStickyTree] = useState({})
const [stickyTree, setStickyTree] = useState([])
const [expandTree, setExpandTree] = useState([])
const handleSwitchAccount = (value) => {
setActiveEmailId(0);
setCurrentMailboxDEI(value)
setStickyTree(deiStickyTree[value] || [])
setExpandTree([`${value}-today`, `${value}-todo`])
const opi = accountListDEIMapped[value].OPI_SN
getOPIEmailDir(opi)
setCurrentMailboxOPI(opi);
@ -140,66 +89,14 @@ function Follow() {
}
}
useEffect(() => {
fetchOrderList({ type: 'today' }, loginUser)
return () => {}
}, [])
// 123456coli_ordertype=7coli_ordertype=8
useEffect(() => {
console.log('orderList render', currentMailboxDEI, currentMailboxOPI)
const byDEI = groupBy(orderList, 'OPI_DEI_SN')
// console.log(byDEI, 'byDEI')
const byState = Object.keys(byDEI).reduce((acc, key) => {
// const stickyIndex0 = byDEI[key].filter(ele => [1, 2, 6].includes(ele.COLI_StateCode))
// const stickyIndex1 = byDEI[key].filter(ele => ![1, 2, 6].includes(ele.COLI_StateCode))
const sticky = groupBy(byDEI[key], (ele) => ([1, 2, 6].includes(ele.coli_ordertype) ? 0 : 1))
// const sticky = groupBy(byDEI[key], ele => [1, 2, 6].includes(ele.COLI_StateCode) ? 'today' : 'todo');
// console.log(sticky, ';;;;');
// const deiName = deptMap.get(`${key}`);
const treeNode = [
{
title: '今日任务',
key: key + '-today',
getMails: false,iconIndex: 'star',
icon: <StarTwoTone />, _raw: {COLI_SN: 0, IsTrue: 0,},
children: (sticky[0] || []).map((o) => ({
key: `today-${o.COLI_SN}`,
title: `(${todoTypes[o.coli_ordertype] || o.COLI_State}) ${o.COLI_ID}`,
iconIndex: 13,
parent: key + '-today',
parentTitle: '今日任务',
parentIconIndex: 'star',
_raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent:-1, IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: -1 },
})),
},
{
title: '待办任务',
key: key + '-todo',
getMails: false,iconIndex: 'calendar',
icon: <CalendarTwoTone />,_raw: {COLI_SN: 0, IsTrue: 0,},
children: (sticky[1] || []).map((o) => ({
key: `todo-${o.COLI_SN}`,
title: `(${todoTypes[o.coli_ordertype] || o.COLI_State}) ${o.COLI_ID}`,
iconIndex: 13,
parent: key + '-todo',
parentTitle: '待办任务',
parentIconIndex: 'calendar',
_raw: { ...o, VKey: o.COLI_SN, VName: o.COLI_ID, VParent:-1, IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: -1 },
})),
},
]
// { key, title: deiName, children: sticky[0] };
return { ...acc, [key]: treeNode }
}, defaultStickyTree)
// console.log('tree 0', byState);
setDeiStickyTree(byState)
const first = currentMailboxDEI || accountDEI[0].value
setExpandTree([`${first}-today`, `${first}-todo`, mailboxActiveNode.key])
setStickyTree(byState[first] || [])
const opi = accountListDEIMapped[first].OPI_SN
setExpandTree([`${opi}-today`, `${opi}-todo`])
return () => {}
}, [orderList])
}, [currentMailboxDEI, mailboxNestedDirsActive])
return (
<>
@ -208,7 +105,7 @@ function Follow() {
<Flex justify='start' align='start' vertical className='h-full'>
<Segmented className='w-full' block shape='round' options={accountDEI} value={currentMailboxDEI} onChange={handleSwitchAccount} />
<div className='overflow-y-auto flex-auto w-full [&_.ant-tree-switcher]:me-0 [&_.ant-tree-node-content-wrapper]:px-0 [&_.ant-tree-node-content-wrapper]:text-ellipsis [&_.ant-tree-node-content-wrapper]:overflow-hidden [&_.ant-tree-node-content-wrapper]:whitespace-nowrap'>
<Tree
<Tree className='[&_.ant-typography-ellipsis]:max-w-44 [&_.ant-typography-ellipsis]:min-w-36'
key='sticky-today'
blockNode
showIcon
@ -219,10 +116,9 @@ function Follow() {
onExpand={(expandedKeys) => setExpandTree(expandedKeys)}
expandedKeys={expandTree}
defaultExpandedKeys={expandTree}
treeData={[...(stickyTree || []), ...mailboxNestedDirsActive]}
// treeData={mergedTree}
treeData={mailboxNestedDirsActive}
icon={(node) => <MailboxDirIcon type={node?.iconIndex} />}
titleRender={(node) => <Typography.Text ellipsis={{ tooltip: node.title }}>{node.title}</Typography.Text>}
titleRender={(node) => <Typography.Text ellipsis={{ tooltip: node.title }} className={`${node?._raw?.IsSuccess===1 ? 'text-primary' : ''}`}>{node.title}</Typography.Text>}
/>
</div>
</Flex>

@ -1,9 +1,10 @@
import { StarTwoTone, CalendarTwoTone, FolderOutlined, DeleteOutlined, ClockCircleOutlined, FormOutlined, DatabaseOutlined } from '@ant-design/icons'
import { StarTwoTone, CalendarTwoTone, FolderOutlined, DeleteOutlined, ClockCircleOutlined, FormOutlined, DatabaseOutlined, BellTwoTone } from '@ant-design/icons'
import { InboxIcon, MailUnreadIcon, SendPlaneFillIcon } from '@/components/Icons'
const EmailDirTypeIcons = {
'star': { component: StarTwoTone, color: '', className: '' },
'calendar': { component: CalendarTwoTone, color: '', className: '' },
'reminder': { component: BellTwoTone, color: '', className: '' },
0: { component: FolderOutlined, color: '#ffe78f', className: 'text-blue-500' },
1: { component: FolderOutlined, color: '#ffe78f', className: 'text-blue-500' },
3: { component: InboxIcon, color: '', className: 'text-indigo-500' },

Loading…
Cancel
Save