From b06b8e7f906994c5cff9c21d7ce60f3c08d5ec29 Mon Sep 17 00:00:00 2001 From: Ycc Date: Sun, 27 Apr 2025 15:01:34 +0800 Subject: [PATCH] =?UTF-8?q?1v1=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.js | 2 +- src/stores/AuthStore.js | 389 +++++++++++++------------- src/views/customer_relation/index.jsx | 73 ++++- 3 files changed, 264 insertions(+), 200 deletions(-) diff --git a/src/config.js b/src/config.js index 9925e28..4acd750 100644 --- a/src/config.js +++ b/src/config.js @@ -21,7 +21,7 @@ export const EMAIL_HOST = 'https://p9axztuwd7x8a7.mycht.cn/mail-server/service-m export const API_HOST = 'https://p9axztuwd7x8a7.mycht.cn/whatsapp_server/v2'; export const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_server'; // prod: export const VONAGE_URL = 'https://p9axztuwd7x8a7.mycht.cn/vonage-server'; // 语音和视频接口: -export const HT3 = process.env.NODE_ENV === 'production' ? 'https://p9axztuwd7x8a7.mycht.cn/ht3' : 'http://127.0.0.1:8000'; +export const HT3 = process.env.NODE_ENV === 'production' ? 'https://p9axztuwd7x8a7.mycht.cn/ht3' : 'https://p9axztuwd7x8a7.mycht.cn/ht3'; export const DATE_FORMAT = 'YYYY-MM-DD'; export const DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; diff --git a/src/stores/AuthStore.js b/src/stores/AuthStore.js index 62b35a7..b36c916 100644 --- a/src/stores/AuthStore.js +++ b/src/stores/AuthStore.js @@ -3,7 +3,7 @@ import { devtools } from 'zustand/middleware' import { fetchJSON } from '@/utils/request' import { isEmpty, isNotEmpty } from '@/utils/commons' import { API_HOST, BUILD_VERSION } from '@/config' -import { usingStorage } from '@/utils/usingStorage'; +import { usingStorage } from '@/utils/usingStorage' export const PERM_MERGE_CONVERSATION = 'merge-conversation' export const PERM_ASSIGN_NEW_CONVERSATION = 'assign-new-conversation' @@ -13,133 +13,9 @@ export const PERM_IMPORT_EMAIL = 'import-email' const WAI_SERVER_KEY = 'G-STR:WAI_SERVER' -const useAuthStore = create(devtools((set, get) => ({ - loginUser: { - userId: -1, - userIdStr: '-1', - username: '', - avatarUrl: '', - mobile: '', - email: '', - openId: '', - accountList: [], - emailList: [], - whatsAppBusiness: '', - accountName: '', - }, - - loginStatus: 0, - - isPermitted: (perm) => { - const { waiServer } = usingStorage(WAI_SERVER_KEY) - const { loginUser } = get() - - if (perm === PERM_USE_WHATSAPP) { - return isNotEmpty(waiServer) // ['370', '143', '495', '404', '383', '227'].includes(loginUser.userId) - } - - if (perm === PERM_USE_EMAL) { - return true//['501', '466', '599', '495', '143', '370', '639', '513', '654', '404', '383', '227'].includes(loginUser.userId) - } - - // 导入邮件消息,需要配置才能使用 - if (perm === PERM_IMPORT_EMAIL && window.localStorage.getItem('PERM_IMPORT_EMAIL')) { - return true - } - - if (perm === PERM_MERGE_CONVERSATION) { - return ['404', '383', '227'].includes(loginUser.userId) - } - - if (perm === PERM_ASSIGN_NEW_CONVERSATION) { - return ['79', '383', '404', '227'].includes(loginUser.userId) - } - // 以上是 Hardcode 判断 - // 动态权限列表参考海外供应商平台实现 - }, - - login: async (authCode) => { - const { setStorage } = usingStorage() - const { saveUserSession, setLoginStatus } = get() - - setLoginStatus(200) - - const json = await fetchJSON( - 'https://p9axztuwd7x8a7.mycht.cn/dingtalk/dingtalkwork/WhatsAppAuth', - { authCode }, - ) - - if (json.errcode === 0 && isNotEmpty(json.result.opisn)) { - // TODO:保存个人 WhatsApp 服务器地址 - const waiServer = json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].wai_server : '' - setStorage('G-STR:WAI_SERVER', waiServer) - set(() => ({ - loginUser: { - userId: json.result.opisn, - userIdStr: json.result?.accountlist - .map((acc) => { - return acc.OPI_SN - }) - .join(','), - emailList: json.result?.emaillist.map(item => { - return { - opi_sn: item.opi_sn, - mat_sn: item.mat_sn, - email: item.email, - default: item.Isdefaultemail == 1, - backup: item.Isbakemail == 1, - } - }), - whatsAppBusiness: json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].whatsapp_waba : '', - whatsAppNo: json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].whatsapp_wa : '', - accountName: json.result.opicode, - username: json.result.nick, - avatarUrl: json.result.avatarUrl, - mobile: '+' + json.result.stateCode + '-' + json.result.mobile, - email: json.result.email, - openId: json.result.openId, - accountList: json.result.accountlist, - }, - })) - saveUserSession() - setLoginStatus(302) - } else { - setLoginStatus(403) - } - }, - - getPrimaryEmail: () => { - const { loginUser } = get() - - const defaultList = loginUser.emailList.filter(email => { - return email.default - }) - - if (defaultList.length > 0) { - return defaultList[0].email - } else { - const backupList = loginUser.emailList.filter(email => { - return email.backup - }) - - if (backupList.length > 0) { - return backupList[0].email - } else if (loginUser.emailList.length > 0) { - return loginUser.emailList[0].email - } - } - }, - - setLoginStatus: (code) => { - set(() => ({ - loginStatus: code, - })) - }, - - logout: () => { - window.sessionStorage.clear() - set(() => ({ - loginStatus: 0, +const useAuthStore = create( + devtools( + (set, get) => ({ loginUser: { userId: -1, userIdStr: '-1', @@ -153,80 +29,203 @@ const useAuthStore = create(devtools((set, get) => ({ whatsAppBusiness: '', accountName: '', }, - })) - }, - - loadUserSession: () => { - let sessionData = window.sessionStorage.getItem('GLOBAL_SALES_LOGIN_USER') - // let sessionData ='{"userId":"155","userIdStr":"155","emailList":[],"whatsAppBusiness":"+8617607730395","whatsAppNo":null,"username":"尹诚诚","avatarUrl":"https://static-legacy.dingtalk.com/media/lADPBE1XYG_HAcDNAgDNAgA_512_512.jpg","mobile":"+86-18507832160","email":"ycc@hainatravel.com","openId":"K8BNXMf8ESSr1DzLVUrX7wiEiE","accountList":[{"OPI_SN":155,"OPI_Code":"YCC","OPI_NameCN":"尹诚诚","OPI_DEI_SN":1,"OPI_NameEN":"Yin Chengcheng"}]}' - - // if (window.location.hostname === '202.103.68.93' && window.location.port === '4173' && isEmpty(sessionData)) { - // sessionData = `{"userId":"383","userIdStr":"383,609","emailList":[{"opi_sn":383,"mat_sn":760,"email":"lyj@asiahighlights.com","default":false,"backup":false},{"opi_sn":383,"mat_sn":759,"email":"lyj@chinahighlights.com","default":true,"backup":false},{"opi_sn":383,"mat_sn":758,"email":"lyj@hainatravel.com","default":false,"backup":false}],"username":"廖一军","avatarUrl":"https://static-legacy.dingtalk.com/media/lALPBDDrhXr716HNAoDNAoA_640_640.png","mobile":"+86-18777396951","email":"lyj@hainatravel.com","whatsAppBusiness":"8617458471254","openId":"iioljiPmZ4RPoOYpkFiSn7IKAiEiE","accountList":[{"OPI_SN":383,"OPI_Code":"LYJ","OPI_NameCN":"廖一军","OPI_DEI_SN":7,"OPI_NameEN":"Jimmy Liow"},{"OPI_SN":609,"OPI_Code":"LYJAH","OPI_NameCN":"廖一军(ah)","OPI_DEI_SN":28,"OPI_NameEN":"Jimmy Liow"}]}` - - // window.localStorage.setItem('GLOBAL_SALES_LOGIN_USER', sessionData) - // } - if (import.meta.env.DEV && isEmpty(sessionData)) { - sessionData = window.localStorage.getItem('GLOBAL_SALES_LOGIN_USER') - } - - if (sessionData !== null) { - const sesstionObj = JSON.parse(sessionData) - set(() => ({ - loginUser: sesstionObj, - })) - } - }, - - saveUserSession: () => { - const { loginUser } = get() - window.sessionStorage.setItem( - 'GLOBAL_SALES_LOGIN_USER', - JSON.stringify(loginUser), - ) - }, - - setWhatsAppProfile: async (userId, whatsAppBusiness, whatsAppNo) => { - const { loginUser, saveUserSession } = get() - const postWABAUrl = `${API_HOST}/v2/set_whatsapp_info` - - const params = {opi_sn: userId, whatsapp_waba: whatsAppBusiness, whatsapp_wa: whatsAppNo.replace(/\D/g, '')}; - - return fetchJSON(postWABAUrl, params) - .then(json => { - if (json.errcode === 0) { + + loginStatus: 0, + + isPermitted: (perm) => { + const { waiServer } = usingStorage(WAI_SERVER_KEY) + const { loginUser } = get() + + if (perm === PERM_USE_WHATSAPP) { + return isNotEmpty(waiServer) // ['370', '143', '495', '404', '383', '227'].includes(loginUser.userId) + } + + if (perm === PERM_USE_EMAL) { + return true //['501', '466', '599', '495', '143', '370', '639', '513', '654', '404', '383', '227'].includes(loginUser.userId) + } + + // 导入邮件消息,需要配置才能使用 + if (perm === PERM_IMPORT_EMAIL && window.localStorage.getItem('PERM_IMPORT_EMAIL')) { + return true + } + + if (perm === PERM_MERGE_CONVERSATION) { + return ['404', '383', '227'].includes(loginUser.userId) + } + + if (perm === PERM_ASSIGN_NEW_CONVERSATION) { + return ['79', '383', '404', '227'].includes(loginUser.userId) + } + // 以上是 Hardcode 判断 + // 动态权限列表参考海外供应商平台实现 + }, + + login: async (authCode) => { + const { setStorage } = usingStorage() + const { saveUserSession, setLoginStatus } = get() + + setLoginStatus(200) + + const json = await fetchJSON('https://p9axztuwd7x8a7.mycht.cn/dingtalk/dingtalkwork/WhatsAppAuth', { authCode }) + + if (json.errcode === 0 && isNotEmpty(json.result.opisn)) { + // TODO:保存个人 WhatsApp 服务器地址 + const waiServer = json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].wai_server : '' + setStorage('G-STR:WAI_SERVER', waiServer) set(() => ({ loginUser: { - ...loginUser, - whatsAppNo: whatsAppNo, - whatsAppBusiness: whatsAppBusiness, - } + userId: json.result.opisn, + userIdStr: json.result?.accountlist + .map((acc) => { + return acc.OPI_SN + }) + .join(','), + emailList: json.result?.emaillist.map((item) => { + return { + opi_sn: item.opi_sn, + mat_sn: item.mat_sn, + email: item.email, + default: item.Isdefaultemail == 1, + backup: item.Isbakemail == 1, + } + }), + whatsAppBusiness: json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].whatsapp_waba : '', + whatsAppNo: json.result.whatsappinfo.length > 0 ? json.result.whatsappinfo[0].whatsapp_wa : '', + accountName: json.result.opicode, + username: json.result.nick, + avatarUrl: json.result.avatarUrl, + mobile: '+' + json.result.stateCode + '-' + json.result.mobile, + email: json.result.email, + openId: json.result.openId, + accountList: json.result.accountlist, + }, })) saveUserSession() + setLoginStatus(302) } else { - throw new Error(json?.errmsg + ': ' + json.errcode) + setLoginStatus(403) } - }) - }, - - sendNotify: async () => { - const { loginUser } = get() - const notifyUrl = 'https://p9axztuwd7x8a7.mycht.cn/dingtalk/dingtalkwork/SendMDMsgByDingRobotToGroup' - - const params = { - groupid: 'cidFtzcIzNwNoiaGU9Q795CIg==', - msgTitle: '有人求助', - msgText: loginUser.username + '上传了销售平台' + BUILD_VERSION + '的日志' - }; - - return fetchJSON(notifyUrl, params) - .then(json => { - if (json.errcode === 0) { - console.info('发送通知成功') + }, + + getPrimaryEmail: () => { + const { loginUser } = get() + + const defaultList = loginUser.emailList.filter((email) => { + return email.default + }) + + if (defaultList.length > 0) { + return defaultList[0].email } else { - throw new Error(json?.errmsg + ': ' + json.errcode) + const backupList = loginUser.emailList.filter((email) => { + return email.backup + }) + + if (backupList.length > 0) { + return backupList[0].email + } else if (loginUser.emailList.length > 0) { + return loginUser.emailList[0].email + } } - }) - }, -}), { name: 'authStore' })) + }, + + setLoginStatus: (code) => { + set(() => ({ + loginStatus: code, + })) + }, + + logout: () => { + window.sessionStorage.clear() + set(() => ({ + loginStatus: 0, + loginUser: { + userId: -1, + userIdStr: '-1', + username: '', + avatarUrl: '', + mobile: '', + email: '', + openId: '', + accountList: [], + emailList: [], + whatsAppBusiness: '', + accountName: '', + }, + })) + }, + + loadUserSession: () => { + let sessionData = + import.meta.env.PROD === 'production' + ? window.sessionStorage.getItem('GLOBAL_SALES_LOGIN_USER') + : '{"userId":"155","userIdStr":"155","emailList":[],"whatsAppBusiness":"+8617607730395","whatsAppNo":null,"username":"尹诚诚","avatarUrl":"https://static-legacy.dingtalk.com/media/lADPBE1XYG_HAcDNAgDNAgA_512_512.jpg","mobile":"+86-18507832160","email":"ycc@hainatravel.com","openId":"K8BNXMf8ESSr1DzLVUrX7wiEiE","accountList":[{"OPI_SN":155,"OPI_Code":"YCC","OPI_NameCN":"尹诚诚","OPI_DEI_SN":1,"OPI_NameEN":"Yin Chengcheng"}]}' + + // if (window.location.hostname === '202.103.68.93' && window.location.port === '4173' && isEmpty(sessionData)) { + // sessionData = `{"userId":"383","userIdStr":"383,609","emailList":[{"opi_sn":383,"mat_sn":760,"email":"lyj@asiahighlights.com","default":false,"backup":false},{"opi_sn":383,"mat_sn":759,"email":"lyj@chinahighlights.com","default":true,"backup":false},{"opi_sn":383,"mat_sn":758,"email":"lyj@hainatravel.com","default":false,"backup":false}],"username":"廖一军","avatarUrl":"https://static-legacy.dingtalk.com/media/lALPBDDrhXr716HNAoDNAoA_640_640.png","mobile":"+86-18777396951","email":"lyj@hainatravel.com","whatsAppBusiness":"8617458471254","openId":"iioljiPmZ4RPoOYpkFiSn7IKAiEiE","accountList":[{"OPI_SN":383,"OPI_Code":"LYJ","OPI_NameCN":"廖一军","OPI_DEI_SN":7,"OPI_NameEN":"Jimmy Liow"},{"OPI_SN":609,"OPI_Code":"LYJAH","OPI_NameCN":"廖一军(ah)","OPI_DEI_SN":28,"OPI_NameEN":"Jimmy Liow"}]}` + + // window.localStorage.setItem('GLOBAL_SALES_LOGIN_USER', sessionData) + // } + if (import.meta.env.DEV && isEmpty(sessionData)) { + sessionData = window.localStorage.getItem('GLOBAL_SALES_LOGIN_USER') + } + + if (sessionData !== null) { + const sesstionObj = JSON.parse(sessionData) + set(() => ({ + loginUser: sesstionObj, + })) + } + }, + + saveUserSession: () => { + const { loginUser } = get() + window.sessionStorage.setItem('GLOBAL_SALES_LOGIN_USER', JSON.stringify(loginUser)) + }, + + setWhatsAppProfile: async (userId, whatsAppBusiness, whatsAppNo) => { + const { loginUser, saveUserSession } = get() + const postWABAUrl = `${API_HOST}/v2/set_whatsapp_info` + + const params = { opi_sn: userId, whatsapp_waba: whatsAppBusiness, whatsapp_wa: whatsAppNo.replace(/\D/g, '') } + + return fetchJSON(postWABAUrl, params).then((json) => { + if (json.errcode === 0) { + set(() => ({ + loginUser: { + ...loginUser, + whatsAppNo: whatsAppNo, + whatsAppBusiness: whatsAppBusiness, + }, + })) + saveUserSession() + } else { + throw new Error(json?.errmsg + ': ' + json.errcode) + } + }) + }, + + sendNotify: async () => { + const { loginUser } = get() + const notifyUrl = 'https://p9axztuwd7x8a7.mycht.cn/dingtalk/dingtalkwork/SendMDMsgByDingRobotToGroup' + + const params = { + groupid: 'cidFtzcIzNwNoiaGU9Q795CIg==', + msgTitle: '有人求助', + msgText: loginUser.username + '上传了销售平台' + BUILD_VERSION + '的日志', + } + + return fetchJSON(notifyUrl, params).then((json) => { + if (json.errcode === 0) { + console.info('发送通知成功') + } else { + throw new Error(json?.errmsg + ': ' + json.errcode) + } + }) + }, + }), + { name: 'authStore' }, + ), +) export default useAuthStore diff --git a/src/views/customer_relation/index.jsx b/src/views/customer_relation/index.jsx index f91da72..6c7102b 100644 --- a/src/views/customer_relation/index.jsx +++ b/src/views/customer_relation/index.jsx @@ -1,7 +1,7 @@ import { Link } from 'react-router-dom' import { Form, Input, Button, DatePicker, Select, Table } from 'antd' import dayjs from 'dayjs' -import { ReadIcon, DeliverIcon, SentIcon ,WaitingIcon,FailedIcon} from '@/components/Icons' +import { ReadIcon, DeliverIcon, SentIcon, WaitingIcon, FailedIcon } from '@/components/Icons' import useCustomerRelationStore from '@/stores/CustomerRelationStore' const { RangePicker } = DatePicker @@ -14,7 +14,7 @@ const statusIconMap = { sent: , failed: , accepted: , - default: + default: , } const Index = () => { @@ -37,6 +37,71 @@ const Index = () => { fetchSearchTasks(formattedValues) } + // 按 crt_template 分组并统计每个非空 msg_status 的数量 + const templateStatusCount = {} + tasksList.forEach((item) => { + const { crt_template, msg_status } = item + if (msg_status !== null && msg_status !== undefined) { + if (!templateStatusCount[crt_template]) { + templateStatusCount[crt_template] = {} + } + if (!templateStatusCount[crt_template][msg_status]) { + templateStatusCount[crt_template][msg_status] = 0 + } + templateStatusCount[crt_template][msg_status]++ + } + }) + + // 计算分组后的百分比并添加计数 + const groupedResult = {} + for (const template in templateStatusCount) { + const total = Object.values(templateStatusCount[template]).reduce((acc, val) => acc + val, 0) + groupedResult[template] = {} + for (const status in templateStatusCount[template]) { + const count = templateStatusCount[template][status] + const percentage = ((count / total) * 100).toFixed(2) + groupedResult[template][`${status}_count`] = count + groupedResult[template][`${status}_percentage`] = `${percentage}%` + } + } + + // 将 groupedResult 转换为表格可用的数据格式 + const groupedData = Object.entries(groupedResult).map(([template, stats]) => { + return { + key: template, + crt_template: template, + ...stats, + } + }) + + // 动态生成表格列 + const groupedColumns = [] + groupedColumns.push({ + title: '模板名称', + dataIndex: 'crt_template', + key: 'crt_template', + }) + + const allStatuses = new Set() + tasksList.forEach((item) => { + if (item.msg_status !== null && item.msg_status !== undefined) { + allStatuses.add(item.msg_status) + } + }) + + allStatuses.forEach((status) => { + groupedColumns.push({ + title: `${status} 计数`, + dataIndex: `${status}_count`, + key: `${status}_count`, + }) + groupedColumns.push({ + title: `${status} 百分比`, + dataIndex: `${status}_percentage`, + key: `${status}_percentage`, + }) + }) + const DATE_RANGE_PRESETS = [ { label: '本周', @@ -127,11 +192,11 @@ const Index = () => { - {/* 搜索结果 */} - 搜索结果总数: {tasksList && tasksList.length} +

统计结果

+
) }