|
|
|
@ -1,16 +1,18 @@
|
|
|
|
|
import React, { useEffect, useState, useRef } from 'react';
|
|
|
|
|
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
|
|
|
|
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
|
|
|
|
import { Input, Button, Empty, Tooltip } from 'antd';
|
|
|
|
|
import { Input, Button, Empty, Tooltip, List } from 'antd';
|
|
|
|
|
import { PlusOutlined, LoadingOutlined, HistoryOutlined, FireOutlined,AudioTwoTone } from '@ant-design/icons';
|
|
|
|
|
import { fetchConversationsList, fetchOrderConversationsList } from '@/actions/ConversationActions';
|
|
|
|
|
import { fetchConversationsList, fetchOrderConversationsList, CONVERSATION_PAGE_SIZE } from '@/actions/ConversationActions';
|
|
|
|
|
import ConversationsNewItem from './ConversationsNewItem';
|
|
|
|
|
import { isEmpty } from '@/utils/commons';
|
|
|
|
|
import { debounce, isEmpty, isNotEmpty, pick } from '@/utils/commons';
|
|
|
|
|
import useConversationStore from '@/stores/ConversationStore';
|
|
|
|
|
import useAuthStore from '@/stores/AuthStore';
|
|
|
|
|
import { useVisibilityState } from '@/hooks/useVisibilityState';
|
|
|
|
|
import ChatListItem from './Components/ChatListItem';
|
|
|
|
|
import ChatListFilter from './Components/ChatListFilter';
|
|
|
|
|
import useStyleStore from '@/stores/StyleStore';
|
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
import { DATETIME_FORMAT } from '@/config';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* []
|
|
|
|
@ -29,19 +31,61 @@ const Conversations = () => {
|
|
|
|
|
const conversationsList = useConversationStore((state) => state.conversationsList);
|
|
|
|
|
const [conversationsListLoading, setConversationsListLoading] = useConversationStore((state) => [state.conversationsListLoading, state.setConversationsListLoading]);
|
|
|
|
|
const addToConversationList = useConversationStore((state) => state.addToConversationList);
|
|
|
|
|
|
|
|
|
|
const closedConversationsList = useConversationStore((state) => state.closedConversationsList);
|
|
|
|
|
const setConversationsList = useConversationStore((state) => state.setConversationsList);
|
|
|
|
|
|
|
|
|
|
const isVisible = useVisibilityState();
|
|
|
|
|
|
|
|
|
|
const [tabSelectedConversation, setTabSelectedConversation] = useState({});
|
|
|
|
|
const [tabCnt, setTabCnt] = useState(-1);
|
|
|
|
|
|
|
|
|
|
async function refreshConversationList() {
|
|
|
|
|
setConversationsListLoading(mobile !== undefined ? true : false);
|
|
|
|
|
const _list = await fetchConversationsList({ opisn: userId });
|
|
|
|
|
addToConversationList(_list);
|
|
|
|
|
const [{ search: searchContent, loadNextPage, ...filterState }, setSearchContent, setFilter] = useConversationStore((state) => [state.filter, state.setFilterSearch, state.setFilter])
|
|
|
|
|
|
|
|
|
|
const [currentLoading, setCurrentLoading] = useState(false);
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async function refreshConversationList(current='', append=false) {
|
|
|
|
|
// setConversationsListLoading(mobile !== undefined ? true : false);
|
|
|
|
|
setConversationsListLoading(true);
|
|
|
|
|
setCurrentLoading(current==='');
|
|
|
|
|
|
|
|
|
|
const [otypeC, v] = filterState.otype ? filterState.otype.split('@') : [];
|
|
|
|
|
const otypeV = v ? parseInt(v) : '';
|
|
|
|
|
const searchParams = {
|
|
|
|
|
keyword: searchContent,
|
|
|
|
|
tags: filterState.tags.join(','),
|
|
|
|
|
olabel: otypeC === 'label' ? otypeV : '',
|
|
|
|
|
ostate: otypeC === 'state' ? otypeV : '',
|
|
|
|
|
intour: otypeC === 'intour' ? otypeV : '',
|
|
|
|
|
session_enable: activeList ? 1 : 0,
|
|
|
|
|
lastpagetime: current ?
|
|
|
|
|
dayjs(current).add(1, 'minutes').format('YYYY-MM-DDTHH:mm:ss')
|
|
|
|
|
: append
|
|
|
|
|
? filterState.lastpagetime
|
|
|
|
|
: '',
|
|
|
|
|
lastactivetime: filterState.lastactivetime,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const _list = await fetchConversationsList({ ...searchParams, opisn: userId });
|
|
|
|
|
|
|
|
|
|
// 搜索时, 清空列表
|
|
|
|
|
if (current) {
|
|
|
|
|
addToConversationList(_list, 'next');
|
|
|
|
|
} else if (append === false) {
|
|
|
|
|
setConversationsList(_list)
|
|
|
|
|
} else {
|
|
|
|
|
addToConversationList(_list, 'next');
|
|
|
|
|
}
|
|
|
|
|
setFilter({
|
|
|
|
|
lastpagetime: _list.length > 0 ? _list[_list.length - 1].lasttime : '',
|
|
|
|
|
loadNextPage: !(_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE),
|
|
|
|
|
// ...((_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE) ? {
|
|
|
|
|
// lastactivetime: dayjs(filterState.lastactivetime).subtract(6, 'months').format(DATETIME_FORMAT),
|
|
|
|
|
// } : {}),
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
setConversationsListLoading(false);
|
|
|
|
|
setCurrentLoading(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
@ -51,21 +95,29 @@ const Conversations = () => {
|
|
|
|
|
return () => {};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const [activeList, setActiveList] = useState(true);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isVisible) {
|
|
|
|
|
refreshConversationList();
|
|
|
|
|
if (isVisible && initialState) {
|
|
|
|
|
refreshConversationList(new Date()); // 无感刷新, 不显示上面的loading,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {};
|
|
|
|
|
}, [isVisible]);
|
|
|
|
|
|
|
|
|
|
const [activeList, setActiveList] = useState(true);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isVisible && initialState) {
|
|
|
|
|
refreshConversationList(); // 显示loading
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {};
|
|
|
|
|
}, [activeList]);
|
|
|
|
|
|
|
|
|
|
const [dataSource, setDataSource] = useState(conversationsList);
|
|
|
|
|
const [listUpdateFlag, setListUpdateFlag] = useState();
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// setDataSource(conversationsList);
|
|
|
|
|
setDataSource(activeList ? conversationsList: closedConversationsList);
|
|
|
|
|
setDataSource(conversationsList);
|
|
|
|
|
// setDataSource(activeList ? conversationsList: closedConversationsList);
|
|
|
|
|
return () => {};
|
|
|
|
|
}, [conversationsList, listUpdateFlag, currentConversation.unread_msg_count]);
|
|
|
|
|
|
|
|
|
@ -147,9 +199,6 @@ const Conversations = () => {
|
|
|
|
|
// switchConversation(item);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const [{ search: searchContent, ...filter }, setSearchContent] =
|
|
|
|
|
useConversationStore((state) => [state.filter, state.setFilterSearch])
|
|
|
|
|
|
|
|
|
|
const searchInputRef = useRef(null);
|
|
|
|
|
|
|
|
|
|
const [newChatModalVisible, setNewChatModalVisible] = useState(false);
|
|
|
|
@ -157,12 +206,13 @@ const Conversations = () => {
|
|
|
|
|
|
|
|
|
|
// const closedVisible = closedConversationsList.length > 0;
|
|
|
|
|
const toggleClosedConversationsList = () => {
|
|
|
|
|
const _active = activeList;
|
|
|
|
|
setDataSource(_active ? closedConversationsList : conversationsList);
|
|
|
|
|
setActiveList(!activeList);
|
|
|
|
|
setCurrentConversation({});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const [searchInput, setSearchInput] = useState(searchContent);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className='flex flex-col h-inherit'>
|
|
|
|
|
<div className='flex gap-1 items-center'>
|
|
|
|
@ -181,49 +231,36 @@ const Conversations = () => {
|
|
|
|
|
className=''
|
|
|
|
|
ref={searchInputRef}
|
|
|
|
|
allowClear
|
|
|
|
|
value={searchContent}
|
|
|
|
|
value={searchInput}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
setSearchContent(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])
|
|
|
|
|
}
|
|
|
|
|
setSearchInput(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) => {
|
|
|
|
|
searchInputRef.current.blur()
|
|
|
|
|
onSwitchConversation(dataSource[tabCnt < 0 ? 0 : tabCnt])
|
|
|
|
|
setTabCnt(-1)
|
|
|
|
|
setTabSelectedConversation({})
|
|
|
|
|
// searchInputRef.current.blur()
|
|
|
|
|
setSearchContent(e.target.value)
|
|
|
|
|
// onSwitchConversation(dataSource[tabCnt < 0 ? 0 : tabCnt])
|
|
|
|
|
// setTabCnt(-1)
|
|
|
|
|
// setTabSelectedConversation({})
|
|
|
|
|
return false
|
|
|
|
|
}}
|
|
|
|
|
placeholder={`名称/号码/订单号${
|
|
|
|
|
conversationsListLoading ? '...' : ''
|
|
|
|
|
}`}
|
|
|
|
|
// onSearch={handleRichSearchConvs}
|
|
|
|
|
onSearch={(v, e, { source }) => setSearchContent(v)}
|
|
|
|
|
placeholder={`名称/号码/订单号${conversationsListLoading ? '...' : ''}`}
|
|
|
|
|
// addonBefore={filterTag}
|
|
|
|
|
// addonBefore={<FilterOutlined />}
|
|
|
|
|
// enterButton={'Filter'}
|
|
|
|
|
/>
|
|
|
|
|
<Tooltip
|
|
|
|
|
key={'conversation-list'}
|
|
|
|
|
title={activeList ? '历史会话' : '活跃会话'}>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={toggleClosedConversationsList}
|
|
|
|
|
icon={
|
|
|
|
|
activeList ? (
|
|
|
|
|
<HistoryOutlined className='text-neutral-500' />
|
|
|
|
|
) : (
|
|
|
|
|
<FireOutlined className=' text-orange-500' />
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
type='text'
|
|
|
|
|
/>
|
|
|
|
|
<Tooltip key={'conversation-list'} title={activeList ? '隐藏会话' : '活跃会话'}>
|
|
|
|
|
<Button onClick={toggleClosedConversationsList} icon={activeList ? <HistoryOutlined className='text-neutral-500' /> : <FireOutlined className=' text-orange-500' />} type='text' />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
{mobile && (
|
|
|
|
|
<AudioTwoTone
|
|
|
|
@ -233,29 +270,46 @@ const Conversations = () => {
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<ChatListFilter onFilterChange={(list) => setDataSource(list)} activeList={activeList} />
|
|
|
|
|
<ChatListFilter
|
|
|
|
|
onFilterChange={(d) => {
|
|
|
|
|
refreshConversationList()
|
|
|
|
|
}}
|
|
|
|
|
activeList={activeList}
|
|
|
|
|
/>
|
|
|
|
|
<div className='flex-1 overflow-x-hidden overflow-y-auto relative'>
|
|
|
|
|
{conversationsListLoading && dataSource.length === 0 ? (
|
|
|
|
|
{/* {mobile && conversationsListLoading && dataSource.length === 0 ? ( */}
|
|
|
|
|
{conversationsListLoading && currentLoading ? (
|
|
|
|
|
<div className='text-center py-2'>
|
|
|
|
|
<LoadingOutlined className='text-primary ' />
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
|
|
{dataSource.map((item) => (
|
|
|
|
|
<ChatListItem
|
|
|
|
|
key={item.sn}
|
|
|
|
|
{...{
|
|
|
|
|
item,
|
|
|
|
|
refreshConversationList,
|
|
|
|
|
setListUpdateFlag,
|
|
|
|
|
onSwitchConversation,
|
|
|
|
|
tabSelectedConversation,
|
|
|
|
|
setNewChatModalVisible,
|
|
|
|
|
setEditingChat,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{dataSource.length === 0 && <Empty description={'无数据'} />}
|
|
|
|
|
<List
|
|
|
|
|
itemLayout='vertical'
|
|
|
|
|
dataSource={dataSource}
|
|
|
|
|
loadMore={
|
|
|
|
|
<div className='text-center pt-3 mb-3 h-8 leading-8 '>
|
|
|
|
|
{!conversationsListLoading && loadNextPage ? (
|
|
|
|
|
<Button onClick={() => refreshConversationList(false, true)} size='small'>load more</Button>
|
|
|
|
|
) : null}
|
|
|
|
|
{conversationsListLoading && <LoadingOutlined className='text-primary ' />}
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
renderItem={(item, index) => (
|
|
|
|
|
<ChatListItem
|
|
|
|
|
key={item.sn}
|
|
|
|
|
{...{
|
|
|
|
|
item,
|
|
|
|
|
refreshConversationList,
|
|
|
|
|
setListUpdateFlag,
|
|
|
|
|
onSwitchConversation,
|
|
|
|
|
tabSelectedConversation,
|
|
|
|
|
setNewChatModalVisible,
|
|
|
|
|
setEditingChat,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<ConversationsNewItem
|
|
|
|
|
initialValues={{ ...editingChat, is_current_order: false }}
|
|
|
|
|