feat: 会话窗口

dev/chat
Lei OT 1 year ago
parent 5d83ce7662
commit 807edcb74f

@ -4,22 +4,26 @@ import { useGetJson } from '@/hooks/userFetch';
export const ConversationContext = createContext();
const API_HOST = 'http://127.0.0.1:4523/m2/3888351-0-default'; // local mock
const URL = {
conversationList: 'http://127.0.0.1:4523/m2/3888351-0-default/142426823',
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.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([]); // 页面上激活的对话
const [conversations, setConversations] = useState({}); // 所有对话
const [currentID, setCurrentID] = useState();
const [conversationsList, setConversationsList] = useState([]);
const [conversationsList, setConversationsList] = useState([]); // 对话列表
const [currentConversation, setCurrentConversation] = useState({
id: '', name: ''
});
const [templates, setTemplates] = useState([]);
const [url, setUrl] = useState(URL.conversationList);
const data = useGetJson(url);
@ -36,10 +40,17 @@ export const useConversations = () => {
return () => {};
}, [data]);
const getTemplates = () => {
setUrl(null); // reset url
setUrl(URL.templates);
}
const switchConversation = (cc) => {
console.log('switch to ', cc.id, cc);
setCurrentID(cc.id);
setCurrentConversation(cc);
setMessages(conversations[cc.id] || []);
};
const addError = (reason) => {
@ -55,9 +66,12 @@ export const useConversations = () => {
const addMessage = (message) => {
setMessages((prevMessages) => [...prevMessages, message]);
addMessageToConversations(currentConversation.id, message);
};
const handleMessage = (data) => {
const { errmsg, result: msgObj } = data;
const msg = data.result;
if (!msg) {
return false;
@ -88,7 +102,11 @@ export const useConversations = () => {
realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server'));
realtimeAPI.keepAlive(); // Ping Server
return { errors, messages, conversationsList, currentConversation, sendMessage, fetchConversations, switchConversation };
return {
errors, messages, conversationsList, currentConversation, sendMessage,
fetchConversations, switchConversation,
templates, setTemplates, getTemplates,
};
}
export const useConversationContext = () => useContext(ConversationContext);

@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { observer } from 'mobx-react';
import { Layout, List, Avatar } from 'antd';
import { Layout, List, Avatar, Flex, Typography } from 'antd';
import Messages from './Components/Messages';
import InputBox from './Components/InputBox';
import ConversationsList from './Components/ConversationsList';
@ -9,7 +9,7 @@ import CustomerProfile from './Components/CustomerProfile';
import { useConversationContext } from '@/stores/ConversationContext';
import './Conversations.css';
import { useAuthContext } from '@/stores/AuthContext.js'
import { useAuthContext } from '@/stores/AuthContext.js';
const { Sider, Content, Header, Footer } = Layout;
@ -23,20 +23,26 @@ const CList = [
*
*/
const ChatWindow = observer(() => {
const { loginUser: currentUser } = useAuthContext()
const { loginUser: currentUser } = useAuthContext();
const { errors, messages, sendMessage, currentConversation } = useConversationContext();
return (
<Layout className='full-height' style={{ maxHeight: 'calc(100% - 150px)', height: 'calc(100% - 150px)' }}>
<Sider width={220} theme={'light'} className='scrollable-column' style={{ height: '70vh' }}>
<ConversationsList />
</Sider>
<Layout className='full-height' style={{ maxHeight: 'calc(100% - 150px)', height: 'calc(100% - 150px)' }}>
<Sider width={240} theme={'light'} className='scrollable-column' style={{ height: '70vh' }}>
<ConversationsList />
</Sider>
<Content className='h70' style={{ maxHeight: '70vh', height: '70vh' }}>
<Layout style={{ height: '100%' }}>
<Header className='ant-layout-sider-light ant-card' style={{ paddingLeft: '10px' }}>
<div>{currentConversation.name}</div>
{/* <List
<Content className='h70' style={{ maxHeight: '70vh', height: '70vh' }}>
<Layout style={{ height: '100%' }}>
<Header className='ant-layout-sider-light ant-card' style={{ padding: '10px', height: 'auto' }}>
<Flex gap={16}>
<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${currentConversation.name}`}>{currentConversation.name}</Avatar>
<Flex vertical={true} justify='space-between'>
<Typography.Text strong>{currentConversation.name}</Typography.Text>
{/* <div> HXY231119017</div> */}
</Flex>
</Flex>
{/* <List
dataSource={[]}
renderItem={(item, ii) => (
<List.Item actions={[<a key='list-loadmore-edit'>mark</a>]}>
@ -53,23 +59,23 @@ const ChatWindow = observer(() => {
</List.Item>
)}
/> */}
</Header>
<Content style={{ maxHeight: '70vh', height: '70vh' }}>
<div className='scrollable-column'>
<Messages />
</div>
</Content>
<Footer className='ant-layout-sider-light' style={{ padding: '10px' }}>
<InputBox onSend={(v) => sendMessage(v)} />
</Footer>
</Layout>
{/* <InputBox onSend={(v) => sendMessage(v)} /> */}
</Content>
</Header>
<Content style={{ maxHeight: '70vh', height: '70vh' }}>
<div className='scrollable-column'>
<Messages />
</div>
</Content>
<Footer className='ant-layout-sider-light' style={{ padding: '10px' }}>
<InputBox onSend={(v) => sendMessage(v)} />
</Footer>
</Layout>
{/* <InputBox onSend={(v) => sendMessage(v)} /> */}
</Content>
<Sider width={300} theme={'light'} className='scrollable-column' style={{ maxHeight: '70vh', height: '70vh' }}>
<CustomerProfile customer={{}} />
</Sider>
</Layout>
<Sider width={300} theme={'light'} className='scrollable-column' style={{ maxHeight: '70vh', height: '70vh' }}>
<CustomerProfile customer={{}} />
</Sider>
</Layout>
);
});

@ -1,30 +1,48 @@
import { useRef, useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { List, Avatar } from 'antd';
import { List, Avatar, Flex } from 'antd';
import { useConversationContext } from '@/stores/ConversationContext';
import { ChatItem, ChatList } from 'react-chat-elements';
/**
* []
*/
const Conversations = observer(({ conversations }) => {
const { switchConversation, conversationsList } = useConversationContext()
const { switchConversation, conversationsList } = useConversationContext();
console.log(conversationsList);
const [chatlist, setChatlist] = useState([]);
useEffect(() => {
setChatlist(
(conversationsList || []).map((item) => ({
...item,
avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${item.name}`,
alt: item.name,
title: item.name,
subtitle: item.lastMessage,
date: item.last_time,
unread: item.new_msgs,
}))
);
return () => {};
}, [conversationsList]);
return (
<List
dataSource={conversationsList || []}
renderItem={(item, ii) => (
<List.Item onClick={() => switchConversation(item)} actions={[<a key='list-loadmore-edit'>mark</a>]}>
<List.Item.Meta
avatar={
<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.name}`} >
{item.name}
</Avatar>
}
title={item.name}
// description='{}'
/>
</List.Item>
)}
/>
<>
<ChatList className='chat-list' dataSource={chatlist} onClick={(item) => switchConversation(item)} />
{/* <List
dataSource={conversationsList || []}
renderItem={(item, ii) => (
// actions={[<a key='list-loadmore-edit'>mark</a>]}
<List.Item onClick={() => switchConversation(item)}>
<List.Item.Meta
avatar={<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.name}`}>{item.name}</Avatar>}
title={item.name}
// description='{}'
/>
</List.Item>
)}
/> */}
</>
);
});
export default Conversations;

@ -1,7 +1,69 @@
import { observer } from 'mobx-react';
import { Card } from 'antd';
import { Card, Flex, Avatar, Typography, Radio, Button } from 'antd';
import { useAuthContext } from '@/stores/AuthContext.js';
import { useConversationContext } from '@/stores/ConversationContext';
import { useGetJson } from '@/hooks/userFetch';
const orderTags = [
{ value: 'potential', label: '潜力' },
{ value: 'important', label: '重点' },
{ label: '休眠', value: 'snooze' },
];
const orderStatus = [
{ value: 'pending', label: '报价中' },
// { value: 'in-progress', label: '' },
{ value: 'lost', label: '丢失' },
{ value: 'later', label: '以后联系' },
];
const { Meta } = Card;
const CustomerProfile = observer(({ customer }) => {
return <Card bordered={false} title={customer.name}>{/* other profile details */}</Card>;
const { errors } = useConversationContext();
const { loginUser: currentUser } = useAuthContext();
const orderInfo = useGetJson('http://127.0.0.1:4523/m2/3888351-0-default/144062941');
const { quotes, contact, last_contact, ...order } = orderInfo || {};
return (
<div className=' divide-x-0 divide-y divide-dotted divide-slate-400/[.24]'>
<Card
bordered={false}
title={order?.order_no}
extra={<Radio.Group size={'small'} options={orderTags} value={'important'} onChange={({ target: { value } }) => {}} optionType='button' buttonStyle={'solid'} />}>
<Meta
title={<Radio.Group size={'small'} options={orderStatus} value={'pending'} onChange={({ target: { value } }) => {}} optionType='button' buttonStyle={'solid'} />}
description={' '}
/>
<Flex gap={16}>
<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${contact?.name}`} />
<Flex vertical={true} justify='space-between'>
<Typography.Text strong>{contact?.name}</Typography.Text>
<div>
{contact?.phone} <span>{contact?.email}</span>{' '}
</div>
{/* <div>{order?.order_no}</div> */}
<div>
{order?.location} <span>{order?.local_datetime}</span>
</div>
<div></div>
</Flex>
</Flex>
</Card>
<Flex vertical={true} className='p-2 '>
<div>最新报价</div>
<p className='m-0 py-2 '>{quotes?.[0]?.name}</p>
<Flex justify={'space-between'} >
<Button size={'small'}>Book</Button>
<Button size={'small'}>报价历史</Button>
</Flex>
</Flex>
<p className='p-2 overflow-auto h-40'>{order?.order_detail}</p>
<Flex vertical={true} className='p-2 '>
<div>沟通记录</div>
<p className='m-0 py-2 '>{quotes?.[0]?.name}</p>
</Flex>
</div>
);
});
export default CustomerProfile;

@ -6,7 +6,7 @@ import { Input, Button } from 'antd';
const InputBox = observer(({ onSend }) => {
const [message, setMessage] = useState('');
const sendMessage = () => {
const onOK = () => {
// console.log(message);
if (typeof onSend === 'function' && message.trim() !== '') {
const msgObj = {
@ -26,7 +26,7 @@ const InputBox = observer(({ onSend }) => {
return (
<div>
<Input.Search placeholder='Type message here' enterButton='Send' size='large' onSearch={sendMessage} value={message} onChange={(e) => setMessage(e.target.value)} />
<Input.Search placeholder='Type message here' enterButton='Send' size='large' onSearch={onOK} value={message} onChange={(e) => setMessage(e.target.value)} />
{/* <Input
placeholder='Type a message'
@ -35,10 +35,10 @@ const InputBox = observer(({ onSend }) => {
onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => {
if (e.shiftKey && e.charCode === 13) {
sendMessage();
onOK();
}
}}
rightButtons={<button onClick={sendMessage}>Send</button>}
rightButtons={<button onClick={onOK}>Send</button>}
/> */}
</div>
);

Loading…
Cancel
Save