Merge branch 'dev/unassign'

dev/timezone
Lei OT 1 year ago
commit 75352ad656

@ -97,6 +97,7 @@ export const fetchConversationsSearch = async (params) => {
? []
: (data || []).map((ele) => ({
...ele,
sn: ele.conversationid,
customer_name: `${ele.whatsapp_name || ''}`.trim(),
whatsapp_name: `${ele.whatsapp_name || ''}`.trim(),
opi_sn: ele.OPI_SN || ele.opi_sn || 0,
@ -127,3 +128,32 @@ export const fetchMessagesHistory = async (params) => {
const data = errcode !== 0 ? [] : result; // _params.pagedir === 'next' ? result.reverse() : result;
return parseRenderMessageList(data);
}
/**
* ------------------------------------------------------------------------------------------------
* 未分配
*/
export const fetchConversationsUnassigned = async (params) => {
const { errcode, result: data } = await fetchJSON(`${API_HOST}/unassigned-conversation`, params);
const list =
errcode !== 0
? []
: (data || []).map((ele) => ({
...ele,
customer_name: `${ele.whatsapp_name || ''}`.trim(),
whatsapp_name: `${ele.whatsapp_name || ''}`.trim(),
opi_sn: ele.OPI_SN || ele.opi_sn || 0,
OPI_Name: `${ele.OPI_Name || ele.opi_name || ''}`.trim(),
dateText: dayjs((ele.last_received_time)).format('MM-DD HH:mm'),
}));
return list;
};
/**
* @param {object} params { opi_sn, conversationid }
*/
export const postAssignConversation = async (params) => {
const { errcode, result } = await fetchJSON(`${API_HOST}/assign_conversation`, params);
return errcode !== 0 ? {} : result;
}

@ -19,6 +19,9 @@ import MobileChat from '@/views/mobile/Chat'
import MobileSecondHeader from '@/views/mobile/SecondHeaderWrapper';
import CustomerProfile from '@/views/Conversations/Online/order/CustomerProfile';
import Unassign from '@/views/ChatUnassign';
import ChatAssign from '@/views/Conversations/ChatAssign';
import DingdingLogin from '@/views/dingding/Login'
import useAuthStore from '@/stores/AuthStore'
import '@/assets/index.css'
@ -39,8 +42,10 @@ const router = createBrowserRouter([
children: [
{ index: true, element: <MobileConversation /> },
{ path: 'm/conversation', element: <MobileConversation /> },
{ path: 'unassign', element: <Unassign /> },
],
},
{ path: 'chat/unassign/:whatsappid/:conversationid', element: <ChatAssign /> },
{ path: 'm/chat/:order_sn', element: <MobileChat /> },
{ path: 'm/chat', element: <MobileChat /> },
{
@ -61,6 +66,8 @@ const router = createBrowserRouter([
{ path: 'order/chat/:order_sn', element: <ChatWindow /> },
{ path: 'order/chat', element: <ChatWindow /> },
{ path: 'account/profile', element: <AccountProfile /> },
{ path: 'chat/unassign/:whatsappid/:conversationid', element: <ChatAssign /> },
{ path: 'chat/unassign', element: <Unassign /> },
],
},
],

@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useState, useEffect } from 'react';
import { Divider, Layout, Flex, Image } from 'antd';
import useFormStore from '@/stores/FormStore';
import SearchForm from './Conversations/History/SearchForm';
@ -6,23 +6,44 @@ import ConversationsList from './Conversations/History/ConversationsList';
import MessagesMatchList from './Conversations/History/MessagesMatchList';
import MessagesList from './Conversations/History/MessagesList';
import ImageAlbumPreview from './Conversations/History/ImageAlumPreview';
import { flush, pick } from '@/utils/commons';
import { fetchConversationsSearch } from '@/actions/ConversationActions';
const { Sider, Content } = Layout;
const Index = (props) => {
const [formValues, setFormValues] = useFormStore((state) => [state.chatHistoryForm, state.setChatHistoryForm]);
const [selectedConversation, setSelectedConversation] = useFormStore((state) => [state.chatHistorySelectChat, state.setChatHistorySelectChat]);
const [conversationsListLoading, setConversationsListLoading] = useState(false);
const [conversationsList, setConversationsList] = useState([]);
const handleSubmit = useCallback((values) => {
setFormValues({ ...values });
}, []);
useEffect(() => {
getConversationsList();
return () => {};
}, [formValues]);
const getConversationsList = async () => {
setConversationsListLoading(true);
const params = flush(pick(formValues, ['opisn', 'whatsapp_id', 'search', 'from_date', 'end_date', 'coli_id']));
const data = await fetchConversationsSearch(params);
setConversationsListLoading(false);
setConversationsList(data);
if (data.length === 1) {
setSelectedConversation(data[0]);
}
};
return (
<>
<SearchForm onSubmit={handleSubmit} initialValues={formValues} />
<Divider plain orientation='left' className='mb-0'></Divider>
<Layout hasSider className='h-screen chathistory-wrapper chatwindow-wrapper' style={{ maxHeight: 'calc(100% - 279px)', height: 'calc(100% - 279px)' }}>
<Sider width={300} theme={'light'} className='h-full overflow-y-auto overflow-x-hidden' style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)' }}>
<ConversationsList />
<ConversationsList {...{ conversationsListLoading, conversationsList, selectedConversation, handleChatItemClick: setSelectedConversation }} />
</Sider>
<Content style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)', minWidth: '360px' }}>
<Flex className='h-full relative'>

@ -0,0 +1,52 @@
import { useCallback, useState, useEffect } from 'react';
import { Divider, Layout, Flex, Image } from 'antd';
import useFormStore from '@/stores/FormStore';
import SearchForm from './Conversations/History/SearchForm';
import ConversationsList from './Conversations/History/ConversationsList';
import MessagesMatchList from './Conversations/History/MessagesMatchList';
import MessagesList from './Conversations/History/MessagesList';
import ImageAlbumPreview from './Conversations/History/ImageAlumPreview';
import { flush, pick } from '@/utils/commons';
import { fetchConversationsSearch, fetchConversationsUnassigned } from '@/actions/ConversationActions';
const { Sider, Content } = Layout;
const Unassign = (props) => {
const [selectedConversation, setSelectedConversation] = useFormStore((state) => [state.chatHistorySelectChat, state.setChatHistorySelectChat]);
const [conversationsListLoading, setConversationsListLoading] = useState(false);
const [conversationsList, setConversationsList] = useState([]);
useEffect(() => {
getConversationsList();
return () => {};
}, []);
const getConversationsList = async () => {
setConversationsListLoading(true);
const params = {};
const data = await fetchConversationsSearch(params);
setConversationsListLoading(false);
setConversationsList(data);
if (data.length === 1) {
setSelectedConversation(data[0]);
}
};
return (
<>
<Layout hasSider className='h-screen chathistory-wrapper chatwindow-wrapper' style={{ maxHeight: 'calc(100% - 279px)', height: 'calc(100% - 279px)' }}>
<Sider width={300} theme={'light'} className='h-full overflow-y-auto overflow-x-hidden' style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)' }}>
<p className='text-center '>未分配会话</p>
<ConversationsList {...{ conversationsListLoading, conversationsList, selectedConversation, handleChatItemClick: setSelectedConversation }} />
</Sider>
<Content style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)', minWidth: '360px' }}>
{/* <Flex className='h-full relative'>
<MessagesMatchList />
<MessagesList />
</Flex>
<ImageAlbumPreview /> */}
</Content>
</Layout>
</>
);
};
export default Unassign;

@ -0,0 +1,55 @@
import { useRef, useState, useEffect } from 'react';
import { Layout, Button } from 'antd';
import MessagesHeader from '@/views/Conversations/Online/MessagesHeader';
import MessagesWrapper from '@/views/Conversations/Online/MessagesWrapper';
import InputComposer from '@/views/Conversations/Online/InputComposer';
import { UnorderedListOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons';
import { useNavigate, useParams } from 'react-router-dom';
import useConversationStore from '@/stores/ConversationStore';
import useAuthStore from '@/stores/AuthStore';
import { useShallow } from 'zustand/react/shallow';
import InputAssign from './InputAssign';
import { fetchConversationsList, fetchConversationsSearch } from '@/actions/ConversationActions';
const { Content, Header, Footer } = Layout;
function ChatAssign() {
const navigate = useNavigate();
const { whatsappid, conversationid } = useParams();
const [currentConversation, setCurrentConversation] = useConversationStore(useShallow((state) => [state.currentConversation, state.setCurrentConversation]));
async function refreshConversationList() {
const _list = await fetchConversationsSearch({ whatsapp_id: whatsappid });
if (_list.length > 0) {
setCurrentConversation(_list[0]);
}
}
useEffect(() => {
refreshConversationList();
return () => {};
}, [whatsappid]);
return (
<>
<Layout className='h-full chatwindow-wrapper mobilechat-wrapper' style={{ maxHeight: 'calc(100vh - 32px)', height: 'calc(100vh - 32px)', minWidth: '360px' }}>
<Header className=' px-2 ant-layout-sider-light ant-card h-auto flex flex-col justify-between gap-1 '>
<MessagesHeader />
<InputAssign className={'block py-2'} initialValues={{ conversationid, whatsappid }} />
</Header>
<Content className='flex-grow bg-whatsapp-bg relative'>
<MessagesWrapper updateRead={false} forceGetMessages={true} />
</Content>
{/* <Footer className='ant-layout-sider-light p-0 h-40'>
<InputAssign mobile />
</Footer> */}
</Layout>
</>
);
}
export default ChatAssign;

@ -1,32 +1,8 @@
import { useEffect, useState } from 'react';
import { Spin } from 'antd';
import { ChatItem } from 'react-chat-elements';
import useFormStore from '@/stores/FormStore';
import { flush, pick } from '@/utils/commons';
import { fetchConversationsSearch } from '@/actions/ConversationActions';
const ConversationsList = ({ ...props }) => {
const [formValues,] = useFormStore(((state) => [state.chatHistoryForm,]));
const [selectedConversation, setSelectedConversation] = useFormStore((state) => [state.chatHistorySelectChat, state.setChatHistorySelectChat]);
const ConversationsList = ({ conversationsListLoading, handleChatItemClick, selectedConversation, conversationsList, ...props }) => {
const [conversationsListLoading, setConversationsListLoading] = useState(false);
const [conversationsList, setConversationsList] = useState([]);
useEffect(() => {
getConversationsList();
return () => {};
}, [formValues]);
const getConversationsList = async () => {
setConversationsListLoading(true);
const params = flush(pick(formValues, ['opisn', 'whatsapp_id', 'search', 'from_date', 'end_date', 'coli_id']));
const data = await fetchConversationsSearch(params);
setConversationsListLoading(false);
setConversationsList(data);
if (data.length === 1) {
setSelectedConversation(data[0]);
}
};
return (
<>
<Spin spinning={conversationsListLoading}>
@ -43,7 +19,7 @@ const ConversationsList = ({ ...props }) => {
// dateString={item.last_received_time}
dateString={item.dateText}
className={String(item.conversationid) === String(selectedConversation.conversationid) ? '__active text-primary bg-neutral-100' : ''}
onClick={() => setSelectedConversation(item)}
onClick={() => handleChatItemClick(item)}
/>
))}
</Spin>

@ -0,0 +1,43 @@
import { useState } from 'react';
import { App, Button, Form, Input } from 'antd';
import SearchInput from '@/components/SearchInput';
import { fetchSalesAgent } from '@/actions/CommonActions';
import { postAssignConversation } from '@/actions/ConversationActions';
const InputAssign = ({ initialValues, ...props }) => {
const { message } = App.useApp();
const [form] = Form.useForm();
const [subLoading, setSubLoading] = useState(false);
async function handleSubmit(values) {
const valuesSub = {
...values,
opi_sn: values.opi_sn.value,
};
setSubLoading(true);
await postAssignConversation(valuesSub);
setSubLoading(false);
message.success('分配成功');
}
return (
<>
<Form layout={'inline'} form={form} initialValues={initialValues} onFinish={handleSubmit} {...props}>
<Form.Item label='分配至' name='opi_sn' rules={[{ required: true, message: '请选择分配的顾问' }]}>
<SearchInput placeholder='搜索顾问, 如Coco' fetchOptions={fetchSalesAgent} allowClear={true} />
</Form.Item>
<Form.Item hidden name='conversationid'>
<Input />
</Form.Item>
<div className='flex justify-end gap-2 items-end'>
{/* <Button type='primary' ghost htmlType='submit' size={'small'}>
分配给我
</Button> */}
<Button type='primary' htmlType='submit' loading={subLoading}>
确认
</Button>
</div>
</Form>
</>
);
};
export default InputAssign;

@ -9,7 +9,7 @@ import { isEmpty } from '@/utils/commons';
import useAuthStore from '@/stores/AuthStore';
import { useVisibilityState } from '@/hooks/useVisibilityState';
const MessagesWrapper = () => {
const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const userId = useAuthStore((state) => state.loginUser.userId);
const [currentConversation, updateCurrentConversation, setCurrentConversation] = useConversationStore(useShallow((state) => [state.currentConversation, state.updateCurrentConversation, state.setCurrentConversation]));
@ -27,7 +27,7 @@ const MessagesWrapper = () => {
const [longListLoading, setLongListLoading] = useState(false);
const [shouldScrollBottom, setShouldScrollBottom] = useState(true);
useEffect(() => {
if (currentConversation.sn && activeMessages.length < 20) {
if (currentConversation.sn && (activeMessages.length < 20 || forceGetMessages !== undefined)) {
getFirstPageMessages(currentConversation);
}
setShouldScrollBottom(true);
@ -43,7 +43,7 @@ const MessagesWrapper = () => {
}, [activeMessages]);
useEffect(() => {
if (isVisible && currentConversation.opi_sn && currentConversation.whatsapp_phone_number && activeMessages.length > 0) {
if (updateRead === true && isVisible && currentConversation.opi_sn && currentConversation.whatsapp_phone_number && activeMessages.length > 0) {
fetchCleanUnreadMsgCount({ opisn: currentConversation.opi_sn, whatsappid: currentConversation.whatsapp_phone_number });
refreshTotalNotify();
}
@ -53,7 +53,7 @@ const MessagesWrapper = () => {
const getFirstPageMessages = async (item) => {
setMsgLoading(true);
const data = await fetchMessages({ opisn: userId, whatsappid: item.whatsapp_phone_number, lasttime: '' });
const data = await fetchMessages({ opisn: forceGetMessages ? '' : userId, whatsappid: item.whatsapp_phone_number, lasttime: '' });
setMsgLoading(false);
receivedMessageList(item.sn, data);
const thisLastTime = data.length > 0 ? data[0].orgmsgtime : '';

Loading…
Cancel
Save