import { fetchJSON, postForm, postJSON } from '@/utils/request'; import { API_HOST, API_HOST_V3, DATE_FORMAT, DATEEND_FORMAT, DATETIME_FORMAT, EMAIL_HOST, EMAIL_HOST_v3 } from '@/config'; import { buildTree, groupBy, isEmpty, objectMapper, omitEmpty, uniqWith } from '@/utils/commons'; import { readIndexDB, writeIndexDB } from '@/utils/indexedDB'; import dayjs from 'dayjs'; import { internalEventEmitter } from '@/utils/EventEmitterService'; export const parseHTMLString = (html, needText = false) => { const parser = new DOMParser() const doc = parser.parseFromString(html, 'text/html') let bodyContent = doc.body.innerHTML // bodyContent = bodyContent.replace(/ { const notificationPayload = payload // - 多个tab const channel = getEmailChangesChannel() channel.postMessage(notificationPayload) // - 当前tab internalEventEmitter.emit(EMAIL_CHANNEL_NAME, notificationPayload) } /** * 获取顾问签名 * @param {object} { opi_sn } */ export const getSalesSignatureAction = async (params) => { try { const { result } = await fetchJSON(`${EMAIL_HOST}/email_sign`, params) const { SignContent: html } = result const bodyContent = parseHTMLString(html); return bodyContent; } catch (error) { return ''; } }; /** * 发送邮件 */ export const postSendEmail = async (body) => { const { attaList=[], atta, content, ...bodyData } = body; bodyData.ordertype = 227001; const formData = new FormData(); Object.keys(bodyData).forEach(function (key) { formData.append(key, bodyData[key]); }); attaList.forEach(function (item) { formData.append('attachment', item); }); const { result } = await postForm(`${EMAIL_HOST}/sendmail`, formData); return result; }; /** * 重发邮件 * @param {object} { mai_sn } */ export const postResendEmailAction = async (body) => { const { attaList, atta, content, ...bodyData } = body; const formData = new FormData(); Object.keys(bodyData).forEach(function (key) { formData.append(key, bodyData[key]); }); return await postForm(`${EMAIL_HOST}/email_resend`, formData); }; const encodeEmailInfo = (info) => { const encodeQuote = (str = '') => str.replace(/"/g, ''); //.replace(//g,'>') const CSsClean = encodeQuote(info.MAI_CS).includes(',') ? encodeQuote(info.MAI_CS).split(',') : encodeQuote(info.MAI_CS).split(';'); const tosClean = (encodeQuote(info.MAI_To).includes(',') ? encodeQuote(info.MAI_To).split(',') : encodeQuote(info.MAI_To).split(';')).concat(CSsClean).filter(s => s); const replyTo = info.MAI_Direction === 1 ? info.MAI_To : info.MAI_From; const replyToAll = (tosClean.length > 1) ? (info.MAI_Direction === 1 ? tosClean.join(',') : [...tosClean, info.MAI_From].join(',')) : (info.MAI_Direction === 1 ? info.MAI_To : info.MAI_From) return { ...info, MAI_From: encodeQuote(info.MAI_From), MAI_To: encodeQuote(info.MAI_To), tos: [...new Set(tosClean)], replyToAll, replyTo, } }; /** * 邮件详情 * @param {object} { mai_sn } */ export const getEmailDetailAction = async (params) => { // const cacheKey = params.mai_sn; // const readCache = await readIndexDB(cacheKey, 'mailinfo', 'mailbox'); // if (!isEmpty(readCache)) { // todo: 除了草稿 // return readCache.data; // } const { result } = await fetchJSON(`${EMAIL_HOST}/getmail`, params); let mailType = result.MailInfo?.[0]?.MAI_ContentType || ''; mailType = mailType === '' && (result.MailContent||'').includes('' : '')); const { html, bodyContent, bodyText } = mailType === 'text/html' ? parseHTMLString(cleanContent, true) : { html: '', bodyContent: '', bodyText: '' }; const attachments = (isEmpty(result?.AttachList) ? [] : result.AttachList).filter(ele => isEmpty(ele.ATI_ContentID) || ele.ATI_ContentID == '0'); const insideAttachments = (isEmpty(result?.AttachList) ? [] : result.AttachList).filter(ele => !isEmpty(ele.ATI_ContentID) && ele.ATI_ContentID != '0'); const ret = { info: { ...encodeEmailInfo(result.MailInfo?.[0] || {}), mailType }, content: mailType === 'text/html' ? html : result.MailContent || '', abstract: bodyText || result.MailContent || '', attachments, insideAttachments, AttachList: isEmpty(result?.AttachList) ? [] : result.AttachList, } // writeIndexDB([{key: cacheKey, data: ret}], 'mailinfo', 'mailbox') return ret; } export const getEmailOrderAction = async ({ colisn }) => { const { errcode, result } = await fetchJSON(`${API_HOST}/getorderinfo`, { colisn }) return errcode === 0 ? { ...result[0], customerDetail: result[0].contact[0] } : {} } /** * 主动收邮件, 单个账户 * @param {object} { opi_sn, } */ export const getEmailFetchAction = async (params) => { const { opi_sn, } = params const { result } = await fetchJSON(`${EMAIL_HOST}/email_fetch`, { opi_sn, }) return result }; /** * 报价信邮件草稿 * @param {object} { sfi_sn, coli_sn, lgc } */ export const getEmailQuotationDraftAction = async (params) => { const { result } = await fetchJSON(`${EMAIL_HOST}/QuotationLetter`, params) return { subject: (result.Subject || ''), content: parseHTMLString((result.MailContent || '').replace(/\r\n/g, '')) } } /** * 单个邮件绑定订单 * @param {object} { conversationid, mai_sn, coli_sn, coli_id, sourcetype } */ export const fetchEmailBindOrderAction = async (params) => { const { errcode, result } = await fetchJSON(`${API_HOST}/mailinfo_bindorder`, params) return errcode === 0 ? true : false; } const todoTypes = { // 1新订单;2WhatsApp未读消息;3需一催;4需二催;5需三催;6未处理邮件;入境提醒coli_ordertype=7,余款提醒coli_ordertype=8 1: '新订单', 2: '未读WhatsApp', 3: '一催', 4: '二催', 5: '三催', 6: '老邮件', 7: '入境提醒', 8: '余款提醒', } /** * 顾问的邮箱目录 * @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 } const { errcode, result } = await fetchJSON(`${API_HOST_V3}/email_dir`, { ...defaultParams, ...params }) 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) const retTree = errcode === 0 ? tree : []; const orderList = groupBy(result, row => `${row.IsTrue}`)?.['0'] || []; return retOrder !== false ? orderList : { [`${params.opi_sn}`]: retTree } }; export const getMailboxCountAction = async (params = { opi_sn: '' }, update = true) => { 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]}); }) 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([{ ...readCacheDir, key: Number(params.opi_sn), tree: _newRoot }], 'dirs', 'mailbox') notifyMailboxUpdate({ type: 'dirs', key: Number(params.opi_sn) }) } return ret; }; 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: , // children: [], COLI_SN: 0, // }, ], }), {}, ) 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') const byState = Object.keys(byOPI).reduce((acc, key) => { const sticky = groupBy(byOPI[key], (ele) => ([1, 6].includes(ele.coli_ordertype) ? 0 : [2, 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: key + '-today', IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: key + '-today' }, })), }, { 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: key + '-todo', IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: key + '-todo' }, })), }, ...(!isEmpty(sticky[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: key + '-reminder', IsTrue: 0, ApplyDate: '', OrderSourceType: 227001, parent: key + '-reminder' }, })), }, ] : []), ] 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' }), ...(userIdStr.split(',').map(_opi => getEmailDirAction({ 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] || [])], treeTimestamp: Date.now() })) writeIndexDB(rootTree, 'dirs', 'mailbox') const _mapped = groupBy(rootTree, 'key') return _mapped[opi_sn]?.[0]?.tree || [] } /** * 获取邮件列表 * @usage 邮件目录下的邮件列表 * @usage 订单的邮件列表 */ export const queryEmailListAction = async ({ opi_sn = '', pagesize = 10, last_id = '', node = {}, } = {}) => { const _params = { vkey: 0, vparent: 0, order_source_type: 0, // 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, opi_sn, }), } _params.mai_senddate1 = dayjs(_params.mai_senddate1).format(DATE_FORMAT) const cacheKey = isEmpty(_params.coli_sn) ? `dir-${node.vkey}` : `order-${node.vkey}`; const { errcode, result } = await fetchJSON(`${API_HOST_V3}/mail_list`, _params) const ret = errcode === 0 ? result : [] if (!isEmpty(ret)) { const listids = [...new Set(ret.map(ele => ele.MAI_SN))]; writeIndexDB([{key: cacheKey, data: listids}], 'maillist', 'mailbox') writeIndexDB(ret.map(ele => ({ data: {...ele, listKey: cacheKey }, key: ele.MAI_SN})), 'listrow', 'mailbox') } return ret; } export const searchEmailListAction = async ({opi_sn = '', mailboxtype = 'ALL', sender = '', receiver = '', subject = '', content=''}={}) => { const formData = new FormData() formData.append('opi_sn', opi_sn) formData.append('mailboxtype', mailboxtype) formData.append('sender', sender) formData.append('receiver', receiver) formData.append('subject', subject) // formData.append('content', content) const { errcode, result } = await postForm(`${API_HOST_V3}/mail_search`, formData) const ret = errcode === 0 ? result : [] notifyMailboxUpdate({ type: 'maillist-search-result', query: [sender, receiver, subject].filter(s => s).join(' '), data: ret.map(ele => ({...ele, key: ele.MAI_SN, showFolder: true })) }) return ret; } 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 }) } } const updateEmailKeyMap = { read: 'MOI_ReadState' }; const updateEmailKeyFun = { read: async (params) => { const readCache = await readIndexDB(params.mai_sn_list, 'listrow', 'mailbox') const updateField = Object.keys(params.set).reduce((a, c) => ({ ...a, [updateEmailKeyMap[c]]: params.set[c] }), {}) writeIndexDB( params.mai_sn_list.map((ele) => ({ data: { ...(readCache.get(ele)?.data || {}), ...updateField }, key: ele })), 'listrow', 'mailbox', ) // 通知邮件列表数据更新 const listKey = readCache.get(params.mai_sn_list[0])?.data?.listKey || ''; const notificationPayload = { type: 'listrow', listKey, affectKeys: params.mai_sn_list } notifyMailboxUpdate(notificationPayload) }, processed: removeFromCurrentList, delete: removeFromCurrentList } /** * 更新邮件属性 */ export const updateEmailAction = async (params = { opi_sn: 0, mai_sn_list: [], set: {} }) => { if (isEmpty(params.mai_sn_list)) { throw new Error('没有需要更新的邮件'); } const { errcode, result } = await postJSON(`${API_HOST_V3}/mail_update`, params) if (errcode === 0 ) { for (const [key, value] of Object.entries(params.set)) { const updateFun = updateEmailKeyFun[key] || (() => {}); updateFun(params) } } getMailboxCountAction({ opi_sn: params.opi_sn }); return errcode === 0 ? result : {} } /** * 获取邮件模板 * @param {object} params - Parameters for the email template request. * @param {number} [params.coli_sn] - Customer order line item serial number. * @param {number} [params.lgc] - Language code. * @param {number} [params.opi_sn] - Order product item serial number. * @param {string} [params.remind_type] - Type of reminder. * @param {number} [params.remind_index] - Index of the reminder. */ export const getReminderEmailTemplateAction = async (params = { coli_sn: 0, lgc: 1, opi_sn: 0, remind_type: '', remind_index: 0 }) => { const { errcode, result } = await fetchJSON(`${API_HOST_V3}/reminder_letter`, params) const { html, bodyContent, bodyText } = parseHTMLString(result?.MailContent, true) ; return errcode === 0 ? {...result, bodyContent }: {} } /** * 保存邮件草稿 * @param {object} body - The body of the email. * @param {boolean} [isDraft=false] - Whether the email is a draft. */ export const saveEmailDraftOrSendAction = async (body, isDraft = false) => { const url = isDraft !== false ? `${API_HOST_V3}/email_draft_save` : `${EMAIL_HOST_v3}/sendmail`; const { attaList=[], atta, content, ...bodyData } = body; bodyData.ordertype = 227001; const formData = new FormData(); Object.keys(bodyData).forEach(function (key) { formData.append(key, bodyData[key] || ''); }); // 附件只传新增的 attaList.filter(ele => !ele.fullPath).forEach(function (item) { formData.append('attachment', item); }); const { errcode, result } = await postForm(url, formData); return errcode === 0 ? (result || {}) : {} }; /** * 删除邮件附件 */ export const deleteEmailAttachmentAction = async (ati_sn_list) => { const { errcode, result } = await postJSON(`${API_HOST_V3}/mail_attachment_delete`, { ati_sn_list }) return errcode === 0 ? result : {} }; export const queryHTOrderListAction = async (params) => { const { errcode, result } = await fetchJSON(`${API_HOST}/query_order`, params) return errcode !== 0 ? [] : result } export const queryOPIOrderAction = async (params) => { const { errcode, result } = await fetchJSON(`${API_HOST}/getdvancedwlorder`, params) return errcode !== 0 ? [] : result };