From 70fbcae715a28482f913489638afda8b5583c1ea Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 23 Jan 2024 18:12:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E7=AA=97=E5=8F=A3;=20?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=87=E6=9C=AC;=20=E8=A7=A3=E6=9E=90Whats?= =?UTF-8?q?App=E4=BF=A1=E6=81=AF=E5=90=84=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/lib/msgUtils.js | 111 +++++ src/stores/ConversationContext.js | 380 +++++++++++++++--- src/views/Conversations/ChatWindow.jsx | 61 ++- .../Components/ConversationsList.jsx | 9 +- .../Components/CreatePayment.jsx | 33 ++ .../Components/CustomerProfile.jsx | 46 ++- .../Conversations/Components/InputBox.jsx | 81 ++-- .../Components/LocalTimeClock.jsx | 39 ++ .../Conversations/Components/Messages.jsx | 141 +++++-- .../Components/QuotesHistory.jsx | 63 +++ .../Conversations/ConversationProvider.jsx | 2 +- src/views/Conversations/Conversations.css | 13 + 13 files changed, 819 insertions(+), 162 deletions(-) create mode 100644 src/lib/msgUtils.js create mode 100644 src/views/Conversations/Components/CreatePayment.jsx create mode 100644 src/views/Conversations/Components/LocalTimeClock.jsx create mode 100644 src/views/Conversations/Components/QuotesHistory.jsx diff --git a/package.json b/package.json index 6f5d71e..9acdfb3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "ahooks": "^3.7.8", "antd": "^5.12.8", "crypto-js": "^4.2.0", + "dayjs": "^1.11.10", + "dayjs-plugin-utc": "^0.1.2", "mobx": "^6.12.0", "mobx-react": "^9.1.0", "react": "^18.2.0", diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js new file mode 100644 index 0000000..72abe63 --- /dev/null +++ b/src/lib/msgUtils.js @@ -0,0 +1,111 @@ +export const sentMsgTypeMapped = { + text: { + type: 'text', + contentToSend: (msg) => ({ type: 'text', from: '+8617607730395', to: '', text: { body: msg.text } }), + contentToRender: (msg) => ({...msg}), + }, + whatsappTemplate: { + type: 'template', + contentToSend: (msg) => ({ + template: { + namespace: msg.whatsappTemplate.namespace, + language: msg.whatsappTemplate.language, + type: msg.whatsappTemplate.type, + components: msg.whatsappTemplate.components, + }, + }), + }, +}; +export const whatsappMsgMapped = { + 'whatsapp.inbound_message.received': { + getMsg: (result) => { + console.log('whatsapp.inbound_message.received', result); + return result?.whatsappInboundMessage || null; + }, + contentToRender: (result) => { + console.log( 'whatsapp.inbound_message.received', result); + const contentObj = result?.whatsappInboundMessage || result; // debug: + return parseRenderMessageItem(contentObj); + }, + }, +}; +export const whatsappMsgTypeMapped = { + text: { type: 'text', data: (msg) => ({ text: msg.text.body }) }, + image: { + type: 'photo', + data: (msg) => ({ + data: { + uri: msg.image.link, + width: 200, + height: 200, + alt: '', + }, + onOpen: () => { + console.log('Open image', msg.image.link); + }, + }), + }, + sticker: { + type: 'photo', + data: (msg) => ({ + data: { + uri: msg.sticker.link, + width: 150, + height: 120, + alt: '', + }, + }), + }, + video: { + type: 'video', + data: (msg) => ({ + data: { + videoURL: msg.video.link, + status: { + click: true, + loading: 0, + download: true, + }, + }, + }), + }, + audio: { + type: 'audio', + data: (msg) => ({ + data: { + audioURL: msg.audio.link, + }, + }), + }, + 'unsupported': { type: 'system', data: (msg) => ({ text: 'Message type is currently not supported.' }) }, + // 'unsupported': { type: 'text', data: (msg) => ({ text: 'Message type is currently not supported.' }) } + // file: 'file', + // location: 'location', + // contact: 'contact', + // 'contact-card': 'contact-card', + // 'contact-card-with-photo': 'contact-card-with-photo', + // 'contact-card-with-photo-and-label': 'contact-card-with-photo-and-label', +}; +export const parseRenderMessageItem = (msg) => { + console.log(msg, '[[[['); + return { + ...(whatsappMsgTypeMapped?.[msg.type]?.data(msg) || {}), + id: msg.id, + sender: msg.from, + type: whatsappMsgTypeMapped?.[msg.type]?.type || 'text', + // title: msg.customerProfile.name, + date: msg.sendTime, + }; +}; +export const parseRenderMessageList = (messages) => { + return messages.map((msg) => { + return { + ...(whatsappMsgTypeMapped?.[msg.type]?.data(msg) || {}), + id: msg.id, + sender: msg.from, + type: whatsappMsgTypeMapped?.[msg.type]?.type || 'text', + // title: msg.customerProfile.name, + date: msg.sendTime, + }; + }); +}; diff --git a/src/stores/ConversationContext.js b/src/stores/ConversationContext.js index 28345f8..92b06e9 100644 --- a/src/stores/ConversationContext.js +++ b/src/stores/ConversationContext.js @@ -1,58 +1,84 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; import { RealTimeAPI } from '@/lib/realTimeAPI'; -import { useGetJson } from '@/hooks/userFetch'; +import { whatsappMsgMapped, sentMsgTypeMapped, parseRenderMessageList } from '@/lib/msgUtils'; +import { groupBy } from '@/utils/utils'; export const ConversationContext = createContext(); +export const useConversationContext = () => useContext(ConversationContext); +export async function fetchJSON(url, data) { + let params = ''; + let ifp = ''; + if (data) { + params = new URLSearchParams(data).toString(); + ifp = params ? '?' : ifp; + } + ifp = url.includes('?') ? '' : ifp; + const host = /^https?:\/\//i.test(url) ? '': ''; // HT_HOST; + const response = await fetch(`${host}${url}${ifp}${params}`); + return await response.json(); +} -const API_HOST = 'http://127.0.0.1:4523/m2/3888351-0-default'; // local mock -const URL = { - conversationList: `${API_HOST}/142426823`, - templates: `${API_HOST}/142952738`, -}; -// const WS_URL = 'ws://202.103.68.144:8888/whatever/'; -const WS_URL = 'ws://202.103.68.157:8888/whatever/'; +// const API_HOST = 'http://202.103.68.144:8888'; +const API_HOST = 'http://202.103.68.144:8888'; +const WS_URL = 'ws://202.103.68.144:8888/whatever/'; +// const WS_URL = 'ws://202.103.68.157:8888/whatever/'; // let realtimeAPI = new RealTimeAPI({ url: URL, protocol: 'aaa' }); let realtimeAPI = new RealTimeAPI({ url: WS_URL, protocol: 'WhatsApp' }); export const useConversations = () => { const [errors, setErrors] = useState([]); - const [messages, setMessages] = useState([]); // 页面上激活的对话 - const [conversations, setConversations] = useState({}); // 所有对话 + const [messages, setMessages] = useState([]); // active conversation + const [conversations, setConversations] = useState({}); // all conversation const [currentID, setCurrentID] = useState(); - const [conversationsList, setConversationsList] = useState([]); // 对话列表 + const [conversationsList, setConversationsList] = useState([]); // open conversations const [currentConversation, setCurrentConversation] = useState({ id: '', name: '' }); - const [templates, setTemplates] = useState([]); - const [url, setUrl] = useState(URL.conversationList); - const data = useGetJson(url); - const fetchConversations = () => { - setUrl(null); // reset url - setUrl(URL.conversationList); - } useEffect(() => { + getConversations(); + // getTemplates(); + return () => {}; + }, []); + + const getConversations = async () => { + const data = await fetchJSON('http://127.0.0.1:4523/m2/3888351-0-default/142426823'); setConversationsList(data); if (data && data.length) { switchConversation(data[0]); } + }; - return () => {}; - }, [data]); - + const templates = AllTemplates.filter(_t => _t.status !== 'REJECTED').map( ele => ({...ele, components: groupBy(ele.components, _c => _c.type.toLowerCase())})); // test: 0 + // const [templates, setTemplates] = useState([]); + const [templatesList, setTemplatesList] = useState([]); + const getTemplates = async () => { + const data = await fetchJSON(`${API_HOST}/listtemplates`); + const canUseTemplates = (data?.result?.items || []).filter(_t => _t.status !== 'REJECTED'); + setTemplatesList(canUseTemplates); + }; - const getTemplates = () => { - setUrl(null); // reset url - setUrl(URL.templates); - } + const [customerOrderProfile, setCustomerProfile] = useState({}); + const getCustomerProfile = async (customerId) => { + console.log(customerId, 'getCustomerProfile'); + const data = await fetchJSON(`http://127.0.0.1:4523/m2/3888351-0-default/144062941`); + setCustomerProfile(data); + }; const switchConversation = (cc) => { console.log('switch to ', cc.id, cc); setCurrentID(cc.id); setCurrentConversation(cc); - setMessages(conversations[cc.id] || []); + // debug: 0 + const _all = all.map((ele) => whatsappMsgMapped['whatsapp.inbound_message.received'].contentToRender(ele)); + setMessages([..._all,...conversations[cc.id] || []]); + // Get customer profile when switching conversation + getCustomerProfile(cc.id); }; + /** + * websocket -------------------------------------------------------------------------------------------- + */ const addError = (reason) => { setErrors(prevErrors => [...prevErrors, { reason }]); } @@ -65,36 +91,30 @@ export const useConversations = () => { }; const addMessage = (message) => { - setMessages((prevMessages) => [...prevMessages, message]); + setMessages((prevMessages) => [ ...prevMessages, message]); addMessageToConversations(currentConversation.id, message); }; const handleMessage = (data) => { - const { errmsg, result: msgObj } = data; + const { errcode, errmsg, result } = data; - const msg = data.result; - if (!msg) { + if (!result) { return false; } - if (typeof msg.type === 'string' && msg.type === 'error') { + if (typeof result.type === 'string' && result.type === 'error') { addError('Error Connecting to Server'); } - addMessage({ ...msg.message, sender: 'other', id: Date.now().toString(16) }); + console.log(result, 'handleMessage------------------'); + const msgObj = whatsappMsgMapped[result.type].getMsg(result); + console.log(msgObj, 'msgObj'); + addMessage(whatsappMsgMapped[result.type].contentToRender(msgObj)); }; - const sendMessage = (msg) => { - const msgObj = { - type: 'message', - message: msg, - }; - realtimeAPI.sendMessage(msgObj); - addMessage(msgObj.message); - // debug: - // const msgObjR = { - // type: 'message', - // message: { type: 'text', text: { body: 'Received: ' + msg.text.body,} }, - // }; - // addMessage({ ...msgObjR.message, sender: 'other', id: Date.now().toString(16) }); + const sendMessage = (msgObj) => { + const contentToSend = sentMsgTypeMapped[msgObj.type].contentToSend(msgObj); + realtimeAPI.sendMessage(contentToSend); + const contentToRender = sentMsgTypeMapped[msgObj.type].contentToRender(msgObj); + addMessage(contentToRender); }; realtimeAPI.onError(addError.bind(null, 'Error')); @@ -104,9 +124,273 @@ export const useConversations = () => { return { errors, messages, conversationsList, currentConversation, sendMessage, - fetchConversations, switchConversation, - templates, setTemplates, getTemplates, + getConversations, switchConversation, + // templates: templatesList, // setTemplates, getTemplates, + templates, // debug: 0 + customerOrderProfile, }; } -export const useConversationContext = () => useContext(ConversationContext); +// test: 0 +const all = [ + { + 'id': '65b06828619a1d82777eb4c6', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBQzNDNzBFNjFCREJBNDIyQjQ2AA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:30:14.000Z', + 'type': 'image', + 'image': { + 'link': + 'https://api.ycloud.com/v2/whatsapp/media/download/934379820978291?sig=t%3D1706059814%2Cs%3D91a79a0e4007ad2f6a044a28307affe663f7f81903b3537bd80e758d3c0d0563&payload=eyJpZCI6IjkzNDM3OTgyMDk3ODI5MSIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNjgyODYxOWExZDgyNzc3ZWI0YzYiLCJtaW1lVHlwZSI6ImltYWdlL2pwZWciLCJzaGEyNTYiOiJPVTJjdkN2eHplMUdMMmQ5NUxyTGVaNmpNb2ZscUZYM1RvcXdTTUNWZkxNPSJ9', + 'id': '934379820978291', + 'sha256': 'OU2cvCvxze1GL2d95LrLeZ6jMoflqFX3ToqwSMCVfLM=', + 'mime_type': 'image/jpeg', + }, + }, + { + 'id': '65b06ce6619a1d8277c97fc0', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBMUJBOUZCODY4NkNBMkM2NUEzAA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:50:29.000Z', + 'type': 'text', + 'text': { + 'body': 'eeee', + }, + }, + { + 'id': '65b06b2f619a1d8277b5ab06', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBRkU0RUZGRUI1OUQzQUFBMEExAA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:43:09.000Z', + 'type': 'audio', + 'audio': { + 'link': + 'https://api.ycloud.com/v2/whatsapp/media/download/901696271448320?sig=t%3D1706060589%2Cs%3Dca75dbd57e4867783390c913491263f07c9738d69c141d4ae622c76df9fa033b&payload=eyJpZCI6IjkwMTY5NjI3MTQ0ODMyMCIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNmIyZjYxOWExZDgyNzdiNWFiMDYiLCJtaW1lVHlwZSI6ImF1ZGlvL29nZzsgY29kZWNzPW9wdXMiLCJzaGEyNTYiOiJoZUNSUDdEMjM3bG9ydkZ4eFhSdHZpU1ZsNDR3Rlk4TytaMFhic2k5cy9rPSJ9', + 'id': '901696271448320', + 'sha256': 'heCRP7D237lorvFxxXRtviSVl44wFY8O+Z0Xbsi9s/k=', + 'mime_type': 'audio/ogg; codecs=opus', + }, + }, + { + 'id': '65b06b12619a1d8277b3c0c4', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBREZEMEM0MURDNjJGREVEQjY3AA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:42:40.000Z', + 'type': 'video', + 'video': { + 'link': + 'https://api.ycloud.com/v2/whatsapp/media/download/742404324517058?sig=t%3D1706060560%2Cs%3D53eeb1508c2103e310fb14a72563a8e07c5a84c7e6192a25f3608ac9bea32334&payload=eyJpZCI6Ijc0MjQwNDMyNDUxNzA1OCIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNmIxMjYxOWExZDgyNzdiM2MwYzQiLCJtaW1lVHlwZSI6InZpZGVvL21wNCIsInNoYTI1NiI6IlNJcjRlZFlPb1BDTGtETEgrVTY2d3dkMDgra2JndFV5OHRDd2RjQU5FaFU9In0', + 'caption': 'and', + 'id': '742404324517058', + 'sha256': 'SIr4edYOoPCLkDLH+U66wwd08+kbgtUy8tCwdcANEhU=', + 'mime_type': 'video/mp4', + }, + }, + { + 'id': '65b06aa7619a1d8277ac806e', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBOTFBOTU5RDE2QjgxQTQ1MEE2AA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:40:53.000Z', + 'type': 'sticker', + 'sticker': { + 'link': + 'https://api.ycloud.com/v2/whatsapp/media/download/1156118002042289?sig=t%3D1706060453%2Cs%3Dfbd5f881856614e35715b1e3e1097b3bbe56f8a36aaa67bfbef25a37d9143d51&payload=eyJpZCI6IjExNTYxMTgwMDIwNDIyODkiLCJ3YWJhSWQiOiIxOTAyOTAxMzQxNTY4ODAiLCJpbmJvdW5kTWVzc2FnZUlkIjoiNjViMDZhYTc2MTlhMWQ4Mjc3YWM4MDZlIiwibWltZVR5cGUiOiJpbWFnZS93ZWJwIiwic2hhMjU2IjoibUNaLzdhNnNaNlRNYTE0WW9rUkNTZnVsdGpZNmFRRVZFNVoxMVRwanNQOD0ifQ', + 'id': '1156118002042289', + 'sha256': 'mCZ/7a6sZ6TMa14YokRCSfultjY6aQEVE5Z11TpjsP8=', + 'mime_type': 'image/webp', + 'animated': false, + }, + }, + { + 'id': '65b06a91619a1d8277aaf05e', + 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBRjUxNzdCQ0FEOTlFQzc5MzQ1AA==', + 'wabaId': '190290134156880', + 'from': '+8613317835586', + 'customerProfile': { + 'name': 'qqs', + }, + 'to': '+8617607730395', + 'sendTime': '2024-01-24T01:40:32.000Z', + 'type': 'unsupported', + 'errors': [ + { + 'code': '131051', + 'title': 'Message type unknown', + 'message': 'Message type unknown', + 'error_data': { + 'details': 'Message type is currently not supported.', + }, + }, + ], + }, +]; + +const AllTemplates = [ + { + "wabaId": "190290134156880", + "name": "say_hi", + "language": "en", + "components": [ + { + "type": "BODY", + "text": "Hi {{customer_name}} I'm {{your_name}} from Asia Highlights.\n\nWe provide *families* and *couples* with personalized and stress-free experiences, whether for _milestone trips_, _birthday trips_, _graduation trips_, or _bucketlist trips_.", + "example": { + "body_text": [ + [ + "Mike", + "Jimmy" + ] + ] + } + }, + { + "type": "BUTTONS", + "buttons": [ + { + "type": "URL", + "text": "Asia Highlights", + "url": "https://www.asiahighlights.com/" + } + ] + } + ], + "category": "UTILITY", + "status": "REJECTED", + "qualityRating": "UNKNOWN", + "reason": "INCORRECT_CATEGORY", + "createTime": "2024-01-23T02:26:33.012Z", + "updateTime": "2024-01-23T02:26:35.397Z", + "statusUpdateEvent": "REJECTED" + }, + { + "wabaId": "190290134156880", + "name": "i_hope_this_message_finds_you_well", + "language": "en", + "components": [ + { + "type": "BODY", + "text": "Hi {{customer_name}}, I hope this message finds you well. Did you see my previous message?", + "example": { + "body_text": [ + [ + "Mike" + ] + ] + } + } + ], + "category": "UTILITY", + "status": "REJECTED", + "qualityRating": "UNKNOWN", + "reason": "INCORRECT_CATEGORY", + "createTime": "2024-01-23T02:22:20.232Z", + "updateTime": "2024-01-23T02:22:22.937Z", + "statusUpdateEvent": "REJECTED" + }, + { + "wabaId": "190290134156880", + "name": "asia_highlights_has_receive_your_inquiry", + "language": "en", + "components": [ + { + "type": "BODY", + "text": "Dear {{customer_name}},\n\nThank you for choosing Asia Highlights. Your inquiry has been submitted to Asia Highlights. One of our travel advisors will respond within 24 hours.", + "example": { + "body_text": [ + [ + "Jimmy Liow" + ] + ] + } + }, + { + "type": "HEADER", + "format": "TEXT", + "text": "Asia highlights has receive your inquiry" + }, + { + "type": "FOOTER", + "text": "Kind regards, Asia Highlights Team" + }, + { + "type": "BUTTONS", + "buttons": [ + { + "type": "URL", + "text": "Asia Highlights", + "url": "https://www.asiahighlights.com/" + } + ] + } + ], + "category": "UTILITY", + "status": "APPROVED", + "qualityRating": "UNKNOWN", + "reason": "NONE", + "createTime": "2024-01-19T05:59:32.933Z", + "updateTime": "2024-01-19T05:59:55.581Z", + "statusUpdateEvent": "APPROVED" + }, + { + "wabaId": "190290134156880", + "name": "hello", + "language": "zh_CN", + "components": [ + { + "type": "BODY", + "text": "你好,这是一个测试程序" + }, + { + "type": "HEADER", + "format": "TEXT", + "text": "Hello 同学" + }, + { + "type": "FOOTER", + "text": "Global Highlights" + }, + { + "type": "BUTTONS", + "buttons": [ + { + "type": "URL", + "text": "about us", + "url": "https://www.globalhighlights.com/" + } + ] + } + ], + "category": "MARKETING", + "status": "APPROVED", + "qualityRating": "UNKNOWN", + "reason": "NONE", + "createTime": "2023-11-17T03:26:10.961Z", + "updateTime": "2023-11-17T13:36:33.623Z", + "statusUpdateEvent": "APPROVED" + } +]; diff --git a/src/views/Conversations/ChatWindow.jsx b/src/views/Conversations/ChatWindow.jsx index db34d65..a1d044a 100644 --- a/src/views/Conversations/ChatWindow.jsx +++ b/src/views/Conversations/ChatWindow.jsx @@ -1,78 +1,67 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { observer } from 'mobx-react'; import { Layout, List, Avatar, Flex, Typography } from 'antd'; import Messages from './Components/Messages'; import InputBox from './Components/InputBox'; import ConversationsList from './Components/ConversationsList'; import CustomerProfile from './Components/CustomerProfile'; +import LocalTimeClock from './Components/LocalTimeClock'; import { useConversationContext } from '@/stores/ConversationContext'; import './Conversations.css'; import { useAuthContext } from '@/stores/AuthContext.js'; +import dayjs from 'dayjs'; +import utc from 'dayjs-plugin-utc'; +dayjs.extend(utc); const { Sider, Content, Header, Footer } = Layout; -const CList = [ - { name: 'Customer_1', label: 'Customer_1', key: 'Customer_1', value: 'Customer_1' }, - { name: 'Customer_2', label: 'Customer_2', key: 'Customer_2', value: 'Customer_2' }, - { name: 'Customer_3', label: 'Customer_3', key: 'Customer_3', value: 'Customer_3' }, - { name: 'Customer_4', label: 'Customer_4', key: 'Customer_4', value: 'Customer_4' }, -]; /** * */ const ChatWindow = observer(() => { const { loginUser: currentUser } = useAuthContext(); - const { errors, messages, sendMessage, currentConversation } = useConversationContext(); + const { sendMessage, currentConversation, customerOrderProfile: orderInfo } = useConversationContext(); + const { quotes, contact, last_contact, ...order } = orderInfo; return ( - - + + - - -
+ + +
{currentConversation.name} - - {currentConversation.name} - {/*
HXY231119017
*/} + + + {currentConversation.name} + {/* {contact?.phone} */} + + + {order?.location} + {/* {customerDateTime} */} + +
- {/* ( - mark]}> - - {item.name} - - } - title={item.name} - description='{最近的消息}' - /> - - )} - /> */}
- +
-