diff --git a/src/actions/CommonActions.js b/src/actions/CommonActions.js index ce53d31..3ec4543 100644 --- a/src/actions/CommonActions.js +++ b/src/actions/CommonActions.js @@ -1,3 +1,10 @@ import { fetchJSON } from '@/utils/request'; import { API_HOST } from '@/config'; +/** + * 顾问列表 + */ +export const fetchSalesAgent = async (q) => { + const { errcode, result } = await fetchJSON(`https://p9axztuwd7x8a7.mycht.cn/service-Analyse2/GetOperatorInfo`, { q }); + return errcode !== 0 ? [] : result.map((ele) => ({ ...ele, label: ele.cn_name, value: ele.op_id })); +}; diff --git a/src/actions/ConversationActions.js b/src/actions/ConversationActions.js index 9ae3144..2668d7b 100644 --- a/src/actions/ConversationActions.js +++ b/src/actions/ConversationActions.js @@ -24,11 +24,18 @@ export const fetchConversationsList = async (params) => { /** * - * @param {object} params { opisn, whatsappid, } + * @param {object} params { opisn, whatsappid, lasttime, pagesize } */ export const fetchMessages = async (params) => { - const { result } = await fetchJSON(`${API_HOST}/getcusmessages`, params); - return parseRenderMessageList(result || []); + const defaultParams = { + opisn: '', + whatsappid: '', + // lasttime: '', + lasttime: '2024-01-01T00:25:30', // test: + pagesize: 100, + }; + const { errcode, result } = await fetchJSON(`${API_HOST}/getcusmessages`, {...defaultParams, ...params}); + return errcode !== 0 ? [] : parseRenderMessageList(result || []); } export const fetchCustomerProfile = async (colisn) => { diff --git a/src/assets/noto-color-emoji.0.woff2 b/src/assets/noto-color-emoji.0.woff2 new file mode 100644 index 0000000..c14f2ea Binary files /dev/null and b/src/assets/noto-color-emoji.0.woff2 differ diff --git a/src/assets/noto-color-emoji.1.woff2 b/src/assets/noto-color-emoji.1.woff2 new file mode 100644 index 0000000..a611cf4 Binary files /dev/null and b/src/assets/noto-color-emoji.1.woff2 differ diff --git a/src/assets/noto-color-emoji.3.woff2 b/src/assets/noto-color-emoji.3.woff2 new file mode 100644 index 0000000..a71512d Binary files /dev/null and b/src/assets/noto-color-emoji.3.woff2 differ diff --git a/src/assets/noto-color-emoji.4.woff2 b/src/assets/noto-color-emoji.4.woff2 new file mode 100644 index 0000000..8b6455e Binary files /dev/null and b/src/assets/noto-color-emoji.4.woff2 differ diff --git a/src/assets/noto-color-emoji.5.woff2 b/src/assets/noto-color-emoji.5.woff2 new file mode 100644 index 0000000..2c27280 Binary files /dev/null and b/src/assets/noto-color-emoji.5.woff2 differ diff --git a/src/assets/noto-color-emoji.6.woff2 b/src/assets/noto-color-emoji.6.woff2 new file mode 100644 index 0000000..af3862b Binary files /dev/null and b/src/assets/noto-color-emoji.6.woff2 differ diff --git a/src/assets/noto-sans.woff2 b/src/assets/noto-sans.woff2 new file mode 100644 index 0000000..b644278 Binary files /dev/null and b/src/assets/noto-sans.woff2 differ diff --git a/src/components/SearchInput.jsx b/src/components/SearchInput.jsx new file mode 100644 index 0000000..cf61f9d --- /dev/null +++ b/src/components/SearchInput.jsx @@ -0,0 +1,46 @@ +import React, { useMemo, useRef, useState } from 'react'; +import { Select, Spin } from 'antd'; +import { debounce } from '@/utils/utils'; + +function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) { + const [fetching, setFetching] = useState(false); + const [options, setOptions] = useState([]); + const fetchRef = useRef(0); + const debounceFetcher = useMemo(() => { + const loadOptions = (value) => { + fetchRef.current += 1; + const fetchId = fetchRef.current; + setOptions([]); + setFetching(true); + fetchOptions(value).then((newOptions) => { + if (fetchId !== fetchRef.current) { + // for fetch callback order + return; + } + setOptions(newOptions); + setFetching(false); + }); + }; + return debounce(loadOptions, debounceTimeout); + }, [fetchOptions, debounceTimeout]); + return ( + + ); +} + +export default DebounceSelect; diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index bb11fe7..ec8ea93 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -213,6 +213,13 @@ export const msgStatusRenderMapped = { 'read': 'read', 'failed': 'failed', }; +export const msgStatusRenderMappedCN = { + 'accepted': '[发送ing]', + 'sent': '[已发送]', + 'delivered': '[已送达]', + 'read': '[已读]', + 'failed': '❗', +}; export const receivedMsgTypeMapped = { ...cloneDeep(whatsappMsgMapped), 'message': { @@ -272,7 +279,7 @@ export const whatsappMsgTypeMapped = { photoURL: msg.image.link, width: '100%', height: 200, - alt: '', + alt: msg.image?.caption || '', }), }, sticker: { @@ -333,6 +340,10 @@ export const whatsappMsgTypeMapped = { audioURL: msg.audio.link, }, }), + renderForReply: (msg) => ({ + id: msg.wamid, + message: '[语音]', + }), }, // unsupported: { type: 'system', data: (msg) => ({ text: 'Message type is currently not supported.' }) }, unsupported: { @@ -367,6 +378,10 @@ export const whatsappMsgTypeMapped = { text: msg.contacts.map((ele) => `${ele.name.formatted_name}: ${ele.phones[0].wa_id}`).join('\n'), data: msg.contacts.map((ele) => ({ id: ele.phones[0].wa_id, wa_id: ele.phones[0].wa_id, name: ele.name.formatted_name })), }), + renderForReply: (msg) => ({ + id: msg.wamid, + message: '[联系人]', + }), }, location: { type: 'location', @@ -381,6 +396,10 @@ export const whatsappMsgTypeMapped = { }, originText: msg.location?.address || '', }), + renderForReply: (msg) => ({ + id: msg.wamid, + message: '[位置]', + }), }, // contact: 'contact', // 'contact-card': 'contact-card', @@ -419,7 +438,9 @@ export const parseRenderMessageItem = (msg) => { whatsapp_name: msg?.customerProfile?.name || '', whatsapp_phone_number: msg.from, whatsapp_msg_type: msg.type, - ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true + statusCN: msgStatusRenderMappedCN[msg?.status || 'failed'], + statusTitle: msgStatusRenderMappedCN[msg?.status || 'failed'], + ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true || isEmpty(msg.messageorigin) ? {} : { reply: { @@ -451,7 +472,7 @@ export const parseRenderMessageList = (messages, conversationid = null) => { type: msgContent.type, ...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }), date: msgContent?.sendTime || msg.msgtime || '', - localDate: msg.msgtime || '', + localDate: (msg.msgtime || '').replace('T', ' '), from: msgContent.from, sender: msgContent.from, senderName: msgContent?.customerProfile?.name || msgContent.from, @@ -461,6 +482,8 @@ export const parseRenderMessageList = (messages, conversationid = null) => { senderName: 'me', status: msgStatusRenderMapped[msgContent?.status || 'failed'], dateString: msgStatusRenderMapped[msgContent?.status || 'failed'] === 'failed' ? `发送失败 ${whatsappError?.[msgContent.errorCode] || msgContent.errorMessage} ❌` : '', + statusCN: msgStatusRenderMappedCN[msgContent?.status || 'failed'], + statusTitle: msgStatusRenderMappedCN[msgContent?.status || 'failed'], } : {}), ...((isEmpty(msg.messageorigin_AsJOSN) && isEmpty(msgContent.context)) diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index 8dcd70a..d780c9e 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -27,7 +27,6 @@ const initialConversationState = { // referenceMsg: {}, - aliOSSToken: {}, }; const templatesSlice = (set) => ({ @@ -144,13 +143,16 @@ const conversationSlice = (set, get) => ({ return set({ conversationsList, activeConversations: conversationsMapped }); }, addToConversationList: (newList) => { - const { activeConversations } = get(); + const { activeConversations, conversationsList } = get(); const conversationsIds = Object.keys(activeConversations); const newConversations = newList.filter((conversation) => !conversationsIds.includes(`${conversation.sn}`)); const newConversationsMapped = newConversations.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {}); + const newListIds = newList.map((chatItem) => `${chatItem.sn}`); + const withoutNew = conversationsList.filter(item => !newListIds.includes(`${item.sn}`)); + return set((state) => ({ - conversationsList: [...newConversations, ...state.conversationsList], + conversationsList: [...newList, ...withoutNew], activeConversations: { ...activeConversations, ...newConversationsMapped }, totalNotify: state.totalNotify + newConversations.map((ele) => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0), })); @@ -284,11 +286,10 @@ export const useConversationStore = create( // state actions addError: (error) => set((state) => ({ errors: [...state.errors, error] })), setInitial: (v) => set({ initialState: v }), - setAliOSSToken: (v) => set({ aliOSSToken: v }), // side effects fetchInitialData: async (userId) => { - const { addToConversationList, setTemplates, setInitial, receivedMessageList, setAliOSSToken } = get(); + const { addToConversationList, setTemplates, setInitial, receivedMessageList } = get(); const conversationsList = await fetchConversationsList({ opisn: userId }); addToConversationList(conversationsList); @@ -298,12 +299,12 @@ export const useConversationStore = create( setInitial(true); - const autoGetMsgs = conversationsList.length > 5 ? 5 : conversationsList.length; - for (let index = 0; index < autoGetMsgs; index++) { - const chatItem = conversationsList[index]; - const msgData = await fetchMessages({ opisn: chatItem.opi_sn, whatsappid: chatItem.whatsapp_phone_number }); - receivedMessageList(chatItem.sn, msgData); - } + // const autoGetMsgs = conversationsList.length > 5 ? 5 : conversationsList.length; + // for (let index = 0; index < autoGetMsgs; index++) { + // const chatItem = conversationsList[index]; + // const msgData = await fetchMessages({ opisn: chatItem.opi_sn, whatsappid: chatItem.whatsapp_phone_number }); + // receivedMessageList(chatItem.sn, msgData); + // } // for (const chatItem of conversationsList) { // } }, diff --git a/src/stores/FormStore.js b/src/stores/FormStore.js index 7fef332..68ef9ef 100644 --- a/src/stores/FormStore.js +++ b/src/stores/FormStore.js @@ -1,13 +1,25 @@ import { create } from 'zustand'; -import { RealTimeAPI } from '@/lib/realTimeAPI'; -import { olog, isEmpty } from '@/utils/utils'; -import { receivedMsgTypeMapped, handleNotification } from '@/lib/msgUtils'; -import { fetchConversationsList, fetchTemplates, fetchMessages } from '@/actions/ConversationActions'; import { devtools } from 'zustand/middleware'; -import { WS_URL } from '@/config'; -export const useFormStore = create(devtools((set, get) => ({ - chatHistory: {}, - setChatHistory: (chatHistory) => set({ chatHistory }), -}))); +export const useFormStore = create( + devtools((set, get) => ({ + // 历史记录页面 + chatHistoryForm: {}, + setChatHistoryForm: (chatHistoryForm) => set({ chatHistoryForm, chatHistorySelectChat: {} }), + chatHistorySelectChat: {}, + setChatHistorySelectChat: (chatHistorySelectChat) => set({ chatHistorySelectChat }), + + // 订单跟踪页面 + orderFollowForm: { + type: 'today', + // orderStatus: '新状态', + // orderNumber: '订单号', + // orderLabel: '订单标签', + // startDate: '走团时间', + }, + setOrderFollowForm: (orderFollowForm) => set({ orderFollowForm }), + orderFollowAdvanceChecked: false, + setOrderFollowAdvanceChecked: (orderFollowAdvanceChecked) => set({ orderFollowAdvanceChecked }), + })) +); export default useFormStore; diff --git a/src/utils/utils.js b/src/utils/utils.js index 5f87ef2..ca4784e 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -311,6 +311,20 @@ export const stringToColour = (str) => { return color; }; +export const debounce = (func, wait, immediate) => { + var timeout; + return function () { + var context = this, + args = arguments; + clearTimeout(timeout); + if (immediate && !timeout) func.apply(context, args); + timeout = setTimeout(function () { + timeout = null; + if (!immediate) func.apply(context, args); + }, wait); + }; +} + export const olog = (text, ...args) => { console.log( `%c ${text} `, diff --git a/src/views/ChatHistory.jsx b/src/views/ChatHistory.jsx index 4cfcfa1..50fc561 100644 --- a/src/views/ChatHistory.jsx +++ b/src/views/ChatHistory.jsx @@ -8,61 +8,15 @@ import { isEmpty } from '@/utils/utils'; import useFormStore from '@/stores/FormStore'; import { useShallow } from 'zustand/react/shallow'; +import { fetchSalesAgent } from '@/actions/CommonActions'; +// import SearchInput from '@/components/SearchInput2'; +import SearchInput from '@/components/SearchInput'; + const { Sider, Content, Header, Footer } = Layout; const { Search } = Input; const { RangePicker } = DatePicker; + // https://media-xsp2-1.cdn.whatsapp.net/v/t61.24694-24/424735646_380563021285029_2962758854250800176_n.jpg?ccb=11-4&oh=01_AdTogiVdUE-ToI9uH-VQKTTLyDbP7bocXUQe1OETOeCgcg&oe=65F7C6AB&_nc_sid=e6ed6c&_nc_cat=104 -const data = [ - { - title: 'Ann', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', - msgTime: '03-04 13:45:29', - content: - 'Hi, this is Ann from China Highlights travel and it is my pleasure to help your Beijing trip. I have sent you an email with the general idea about you trip. Please check it and if any question or new idea just let me know. We are specailized in customizing tour and I would like to work with you to create a tour itinerary that you like. Look forward to your reply.', - }, - { - title: 'David Azhari', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', - msgTime: '03-04 16:33:08', - content: 'Hi! I just replied to your email saying that I have a few questions and notes', - }, - { - title: 'David Azhari', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', - msgTime: '03-04 16:33:34', - content: 'So first, is it possible for you guys to write a PU Invitation letter for visas or no?', - }, - { - title: 'Ann', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', - msgTime: '03-04 16:33:41', - content: 'you prefer we discuss here or go on by mail ?', - }, - { - title: 'David Azhari', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', - msgTime: '03-04 16:34:03', - content: 'I prefer by mail if it’s ok with you', - }, - { - title: 'Ann', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', - msgTime: '03-04 16:34:28', - content: 'both is okay ,I am typing mail to you now lol and receive your message here.', - }, - { - title: 'David Azhari', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', - msgTime: '03-05 02:56:37', - content: 'Ok thank you so much', - }, - { - title: 'Ann', - avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', - msgTime: '03-04 16:44:03', - content: 'you are welcome I have sent the mail to you ,please check.it is my pleasure to assist you with your tour.', - }, -]; // eslint-disable-next-line react/display-name const SearchForm = memo(function ({ initialValues, onSubmit }) { @@ -79,47 +33,8 @@ const SearchForm = memo(function ({ initialValues, onSubmit }) { style={{ maxWidth: 'none', }}> - - [state.chatHistory, state.setChatHistory]), - ) - + const [formValues, setFormValues] = useFormStore(useShallow((state) => [state.chatHistoryForm, state.setChatHistoryForm])); + const [selectedConversation, setSelectedConversation] = useFormStore(useShallow((state) => [state.chatHistorySelectChat, state.setChatHistorySelectChat])); const handleSubmit = useCallback((values) => { setFormValues({ ...values }); @@ -199,12 +112,11 @@ function ChatHistory() { const [loading, setLoading] = useState(false); const [conversationsList, setConversationsList] = useState([]); - const [selectedConversation, setSelectedConversation] = useState({}); const [chatItemMessages, setChatItemMessages] = useState([]); const getConversationsList = async () => { setLoading(true); setChatItemMessages([]); - const data = await fetchConversationsList({ opisn: formValues.travel }); + const data = await fetchConversationsList({ opisn: formValues.agent?.value || -1 }); setLoading(false); setConversationsList(data); if (data.length === 1) { @@ -239,7 +151,7 @@ function ChatHistory() { const parts = str.split(/(https?:\/\/[^\s]+|\p{Emoji_Presentation})/gmu).filter((s) => s !== ''); const links = str.match(/https?:\/\/[\S]+/gi) || []; const emojis = str.match(/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g) || []; - const extraClass = isEmpty(emojis) ? '' : 'text-base leading-5 emoji-text'; + const extraClass = isEmpty(emojis) ? '' : ''; const objArr = parts.reduce((prev, curr, index) => { if (links.includes(curr)) { prev.push({ type: 'link', key: curr }); @@ -251,16 +163,15 @@ function ChatHistory() { return prev; }, []); return ( - + {(objArr || []).map((part, index) => { if (part.type === 'link') { return ( - + {part.key} ); } else { - // if (part.type === 'emoji') return part.key; } })} @@ -290,7 +201,7 @@ function ChatHistory() { }; // eslint-disable-next-line react/display-name const MessageBoxWithRef = forwardRef((props, ref) => ( -
  • +
  • )); @@ -343,10 +254,13 @@ function ChatHistory() { onTitleClick={() => handlePreview(message)} notch={false} text={} + copiableDate={true} dateString={message.dateString || message.localDate} - className={['whitespace-pre-wrap', message.whatsapp_msg_type === 'sticker' ? 'bg-transparent' : '', message.sender === 'me' ? 'whatsappme-container' : ''].join( - ' ' - )} + className={[ + 'whitespace-pre-wrap mb-2', + message.whatsapp_msg_type === 'sticker' ? 'bg-transparent' : '', + message.sender === 'me' ? 'whatsappme-container' : '', + ].join(' ')} style={{ backgroundColor: message.sender === 'me' ? '#ccd4ae' : '#fff', }} @@ -362,6 +276,15 @@ function ChatHistory() { ], } : {})} + renderAddCmp={ +
    + {message.senderName} + {message.dateString || message.localDate} + {message.statusCN} +
    + } + // date={null} + // status={null} /> )} /> diff --git a/src/views/Conversations/Components/ConversationsList.jsx b/src/views/Conversations/Components/ConversationsList.jsx index fa19402..c7f4665 100644 --- a/src/views/Conversations/Components/ConversationsList.jsx +++ b/src/views/Conversations/Components/ConversationsList.jsx @@ -19,14 +19,16 @@ const Conversations = () => { const userId = useAuthStore((state) => state.loginUser.userId); const initialState = useConversationStore((state) => state.initialState); const activeConversations = useConversationStore((state) => state.activeConversations); - const currentConversation = useConversationStore((state) => state.currentConversation); + const [currentConversation, setCurrentConversation] = useConversationStore((state) => [state.currentConversation, state.setCurrentConversation]); const conversationsList = useConversationStore((state) => state.conversationsList); const addToConversationList = useConversationStore((state) => state.addToConversationList); const delConversationitem = useConversationStore((state) => state.delConversationitem); - const setCurrentConversation = useConversationStore((state) => state.setCurrentConversation); const receivedMessageList = useConversationStore((state) => state.receivedMessageList); const setMsgLoading = useConversationStore((state) => state.setMsgLoading); + const [tabSelectedConversation, setTabSelectedConversation] = useState({}); + const [tabCnt, setTabCnt] = useState(-1); + const [dataSource, setDataSource] = useState(conversationsList); useEffect(() => { setDataSource(conversationsList); @@ -46,16 +48,23 @@ const Conversations = () => { const getOrderConversationList = async (colisn) => { const { whatsapp_phone_number } = switchToC; const whatsappID = coli_guest_WhatsApp || whatsapp_phone_number || ''; - let findCurrent = conversationsList.findIndex((item) => item.coli_sn === Number(colisn)); - if (!isEmpty(whatsappID)) { + let findCurrentOrderChats = conversationsList.filter((item) => `${item.coli_sn}` === `${colisn}`); + let findCurrentIndex = isEmpty(findCurrentOrderChats) ? -1 : 0; // findCurrentOrderChats.length-1; + let findCurrent = findCurrentOrderChats[findCurrentIndex]; + if (findCurrentIndex !== -1) { + addToConversationList(findCurrentOrderChats); + } else if (!isEmpty(whatsappID)) { const data = await fetchOrderConversationsList({ opisn: userId, colisn: colisn, whatsappid: whatsappID }); if (!isEmpty(data)) { addToConversationList(data); + findCurrentIndex = 0; // data.length-1; // data.lastIndexOf((item) => item.coli_sn === Number(colisn)); + findCurrent = data[findCurrentIndex]; + } else { + // findCurrentIndex = conversationsList.findIndex((item) => item.coli_sn === Number(colisn)); // data.findIndex((item) => item.sn === currentConversation.sn); } - findCurrent = conversationsList.findIndex((item) => item.coli_sn === Number(colisn)); // data.findIndex((item) => item.sn === currentConversation.sn); } - if (findCurrent !== -1) { - switchConversation(conversationsList[findCurrent]); + if (findCurrentIndex >= 0) { + switchConversation(findCurrent); } else { // reset chat window setCurrentConversation({ sn: '', customer_name: '', coli_sn: order_sn }); @@ -64,7 +73,7 @@ const Conversations = () => { }; const getMessages = async (item) => { setMsgLoading(true); - const data = await fetchMessages({ opisn: userId, whatsappid: item.whatsapp_phone_number }); + const data = await fetchMessages({ opisn: userId, whatsappid: item.whatsapp_phone_number, lasttime: '2024-01-01T00:25:30' }); setMsgLoading(false); receivedMessageList(item.sn, data); }; @@ -130,11 +139,23 @@ const Conversations = () => { onChange={(e) => { setSearchContent(e.target.value); handleSearchConversations(e.target.value); + setTabCnt(-1); + setTabSelectedConversation({}); + }} + onKeyDown={e => { + if (e.key === 'Tab') { + e.preventDefault(); + const _this = tabCnt >= dataSource.length-1 ? 0 : tabCnt + 1 + setTabCnt(_this); + setTabSelectedConversation(dataSource[_this]); + } }} onPressEnter={(e) => { handleSearchConversations(e.target.value); searchInputRef.current.blur(); - onSwitchConversation(dataSource[0]); + onSwitchConversation(dataSource[(tabCnt < 0 ? 0 : tabCnt)]); + setTabCnt(-1); + setTabSelectedConversation({}); return false; }} placeholder='搜索名称' @@ -169,7 +190,9 @@ const Conversations = () => { date={item.last_received_time} unread={item.unread_msg_count} className={ - String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-4 border-solid border-whatsapp-bg bg-whatsapp-bg' : '' + [String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-4 border-solid border-whatsapp-bg bg-whatsapp-bg' : '', + String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '' + ].join(' ') } onClick={() => onSwitchConversation(item)} /> diff --git a/src/views/Conversations/Components/MessagesList.jsx b/src/views/Conversations/Components/MessagesList.jsx index 6902482..8435a0b 100644 --- a/src/views/Conversations/Components/MessagesList.jsx +++ b/src/views/Conversations/Components/MessagesList.jsx @@ -38,8 +38,8 @@ const MessagesList = ({ messages, handlePreview, reference }) => { const scrollToMessage = (id, index) => { const _i = index || messages.findIndex((msg) => msg.id === id); - if (reference.current && messageRefs.current[_i]) { - reference.current.scrollTop = messageRefs.current[_i].offsetTop; + if (_i >= 0) { + messageRefs.current[_i].scrollIntoView({ behavior: 'smooth', block: 'start' }); } }; @@ -61,7 +61,7 @@ const MessagesList = ({ messages, handlePreview, reference }) => { const parts = str.split(/(https?:\/\/[^\s]+|\p{Emoji_Presentation})/gmu).filter((s) => s !== ''); const links = str.match(/https?:\/\/[\S]+/gi) || []; const emojis = str.match(/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g) || []; - const extraClass = isEmpty(emojis) ? '' : 'text-base leading-5 emoji-text'; + const extraClass = isEmpty(emojis) ? '' : ''; const objArr = parts.reduce((prev, curr, index) => { if (links.includes(curr)) { prev.push({ type: 'link', key: curr }); @@ -73,11 +73,11 @@ const MessagesList = ({ messages, handlePreview, reference }) => { return prev; }, []); return ( - + {(objArr || []).map((part, index) => { if (part.type === 'link') { return ( - + {part.key} ); diff --git a/src/views/Conversations/Conversations.css b/src/views/Conversations/Conversations.css index 5d345b5..64bd1c5 100644 --- a/src/views/Conversations/Conversations.css +++ b/src/views/Conversations/Conversations.css @@ -1,3 +1,24 @@ +@font-face { + font-family: 'Noto Sans'; + src: url('/src/assets/noto-sans.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.0.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.1.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} @font-face { font-family: 'Noto Color Emoji'; src: url('/src/assets/noto-color-emoji.2.woff2') format('woff2'); @@ -5,6 +26,34 @@ font-style: normal; font-display: swap; } +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.3.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.4.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.5.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Noto Color Emoji'; + src: url('/src/assets/noto-color-emoji.6.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} @font-face { font-family: 'Noto Color Emoji'; src: url('/src/assets/noto-color-emoji.7.woff2') format('woff'); @@ -53,7 +102,7 @@ } .chatwindow-wrapper .bg-transparent .rce-mbox{ background: unset; - box-shadow: none; + /* box-shadow: none; */ } .chatwindow-wrapper .bg-transparent .rce-mbox-left-notch, .chatwindow-wrapper .bg-transparent .rce-mbox-right-notch @@ -66,11 +115,12 @@ color: #00000073; } .chatwindow-wrapper .rce-mbox-text .emoji-text, +.chatwindow-wrapper .rce-mbox-time, .chatwindow-wrapper .referrer-msg, .chatwindow-wrapper .rce-mbox-reply-message, .chatwindow-wrapper .emoji { - font-family: "Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', Arial, sans-serif; + font-family: 'Noto Sans',"Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', Arial, sans-serif; font-weight: 500; } .chatwindow-wrapper .rce-mbox-text a{ @@ -84,6 +134,9 @@ } .chatwindow-wrapper .rce-mbox-photo .rce-mbox-text{ padding-left: 8px; + padding-right: 8px; + margin: unset; + max-width: unset; } .chatwindow-wrapper .rce-mbox-left-notch { width: 10px; @@ -120,3 +173,10 @@ { display: inline-block; } + +/** Chat history */ +/* .chathistory-wrapper .rce-mbox-time { + user-select: auto; + -webkit-user-select: auto; + -moz-user-select: auto; +}*/ diff --git a/src/views/OrderFollow.jsx b/src/views/OrderFollow.jsx index 921f4a9..755c7b3 100644 --- a/src/views/OrderFollow.jsx +++ b/src/views/OrderFollow.jsx @@ -9,10 +9,13 @@ import { Conditional } from '@/components/Conditional' import useOrderStore from '@/stores/OrderStore' import useAuthStore from '@/stores/AuthStore' import { copy } from '@/utils/commons' +import useFormStore from '@/stores/FormStore'; +import { useShallow } from 'zustand/react/shallow'; const { RangePicker } = DatePicker -const AdvanceSearchForm = memo(function ({ onSubmit }) { +// eslint-disable-next-line react/display-name +const AdvanceSearchForm = memo(function ({ initialValues, onSubmit }) { const DATE_RANGE_PRESETS = [ { @@ -49,7 +52,7 @@ const AdvanceSearchForm = memo(function ({ onSubmit }) { layout={'vertical'} form={form} initialValues={{ - orderLabel: '', orderStatus: '', remindState: '' + orderLabel: '', orderStatus: '', remindState: '', ...initialValues }} onFinish={handleSubmit} > @@ -164,7 +167,7 @@ function OrderGroupTable({ formValues }) { if (record.buytime > 0) regularText = '(R' + record.buytime + ')' return ( - {text + regularText} + {text + regularText} { let canSearch = true @@ -301,7 +304,7 @@ function OrderGroupTable({ formValues }) { children: - } + } ) }) @@ -311,14 +314,16 @@ function OrderGroupTable({ formValues }) { function OrderFollow() { - const [advanceChecked, toggleAdvance] = useState(false) - const [formValues, setFormValues] = useState({ - type: 'today', - orderStatus: '新状态', - orderNumber: '订单号', - orderLabel: '订单标签', - startDate: '走团时间' - }) + // const [advanceChecked, toggleAdvance] = useState(false) + // const [formValues, setFormValues] = useState({ + // type: 'today', + // orderStatus: '新状态', + // orderNumber: '订单号', + // orderLabel: '订单标签', + // startDate: '走团时间' + // }) + const [formValues, setFormValues] = useFormStore(useShallow((state) => [state.orderFollowForm, state.setOrderFollowForm])); + const [advanceChecked, toggleAdvance] = useFormStore(useShallow((state) => [state.orderFollowAdvanceChecked, state.setOrderFollowAdvanceChecked])); const handleSubmit = useCallback((values) => { setFormValues({ ...values, type: 'advance' }) @@ -352,7 +357,7 @@ function OrderFollow() { defaultChecked={false} onChange={() => { toggleAdvance(!advanceChecked) }} /> - } + } />