Merge remote-tracking branch 'origin/main' into dev/chat

dev/chat
Lei OT 2 years ago
commit 45a49a89a2

@ -20,65 +20,42 @@ function AuthApp() {
const href = useHref() const href = useHref()
useEffect(() => {
if (loginUser.userId > 0) {
useConversationStore.getState().connectWebsocket(loginUser.userId)
useConversationStore.getState().fetchInitialData(loginUser.userId) // userIdStr
}
return () => {
useConversationStore.getState().disconnectWebsocket();
}
}, [])
useEffect(() => { useEffect(() => {
// /p... // /p...
if ((loginUser.userId === -1) && (href.indexOf('/p/') === -1)) { if ((loginUser.userId === -1) && (href.indexOf('/p/') === -1)) {
navigate('/p/dingding/qrcode'); navigate('/p/dingding/qrcode')
} }
}, [href]) }, [href])
const totalNotify = useConversationStore((state) => state.totalNotify);
const [connectWebsocket, fetchInitialData, disconnectWebsocket ] = useConversationStore((state) => [
state.connectWebsocket,
state.fetchInitialData,
state.disconnectWebsocket,
]);
useEffect(() => { useEffect(() => {
if (loginUser.userId > 0) { if (loginUser.userId > 0) {
connectWebsocket(loginUser.userId); useConversationStore.getState().connectWebsocket(loginUser.userId)
fetchInitialData(loginUser.userId); // userIdStr useConversationStore.getState().fetchInitialData(loginUser.userId) // userIdStr
} }
return () => { return () => {
disconnectWebsocket(); useConversationStore.getState().disconnectWebsocket()
} }
}, []) }, [])
useEffect(() => { useEffect(() => {
Notification.requestPermission(); Notification.requestPermission()
return () => {}; return () => {}
}, []) }, [])
let defaultPath = '/order/follow'
if (href !== '/') {
const splitPath = href.split('/')
if (splitPath.length > 2) {
defaultPath = '/' + splitPath[1] + '/' + splitPath[2]
}
}
const { const {
token: { colorBgContainer }, token: { colorBgContainer },
} = theme.useToken() } = theme.useToken()
/**
* 标签页标题闪烁
*/
const [isTitleVisible, setIsTitleVisible] = useState(true);
useEffect(() => {
let interval;
if (totalNotify > 0) {
interval = setInterval(() => {
document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '______________';
setIsTitleVisible(!isTitleVisible);
}, 500);
} else {
document.title = '聊天式销售平台';
}
return () => clearInterval(interval);
}, [totalNotify, isTitleVisible]);
return ( return (
<ConfigProvider <ConfigProvider
theme={{ theme={{

@ -1,10 +1,10 @@
import { LinkOutlined, MailOutlined, PhoneOutlined, UserOutlined, WhatsAppOutlined } from '@ant-design/icons' import { LinkOutlined, MailOutlined, PhoneOutlined, UserOutlined, WhatsAppOutlined } from '@ant-design/icons'
import { App, Card, Flex, Select, Typography, Empty, Button } from 'antd' import { App, Button, Card, Empty, Flex, Select, Typography } from 'antd'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useParams } from 'react-router-dom'
import useOrderStore from '@/stores/OrderStore' import { Conditional } from '@/components/Conditional'
import useConversationStore from '@/stores/ConversationStore' import useConversationStore from '@/stores/ConversationStore'
import useOrderStore from '@/stores/OrderStore'
import QuotesHistory from './QuotesHistory' import QuotesHistory from './QuotesHistory'
const CustomerProfile = (() => { const CustomerProfile = (() => {
@ -77,10 +77,17 @@ const CustomerProfile = (() => {
</Card> </Card>
<Flex vertical={true} className='p-2 '> <Flex vertical={true} className='p-2 '>
<Typography.Text strong>最新报价</Typography.Text> <Typography.Text strong>最新报价</Typography.Text>
<p className='m-0 py-2 line-clamp-2 '><a target='_blank' href={lastQuotation.letterurl}><LinkOutlined />&nbsp;{lastQuotation.lettertitle}</a></p> <Conditional
<Flex justify={'space-between'} > condition={quotationList.length > 0}
<QuotesHistory dataSource={quotationList} /> whenFalse={<Empty description={<span>暂无报价</span>}></Empty>}
</Flex> whenTrue={
<>
<p className='m-0 py-2 line-clamp-2 '><a target='_blank' href={lastQuotation.letterurl}><LinkOutlined />&nbsp;{lastQuotation.lettertitle}</a></p>
<Flex justify={'space-between'} >
<QuotesHistory dataSource={quotationList} />
</Flex>
</>
}/>
</Flex> </Flex>
<p className='p-2 shadow-inner overflow-auto m-0 break-words whitespace-pre-wrap' dangerouslySetInnerHTML={{__html: orderDetail.order_detail}}></p> <p className='p-2 shadow-inner overflow-auto m-0 break-words whitespace-pre-wrap' dangerouslySetInnerHTML={{__html: orderDetail.order_detail}}></p>
</div> </div>
@ -88,11 +95,7 @@ const CustomerProfile = (() => {
} else { } else {
return ( return (
<Empty <Empty
description={ description={<span>暂无相关订单</span>}
<span>
暂无相关订单
</span>
}
> >
<Button type='primary' onClick={() => { <Button type='primary' onClick={() => {
notification.info({ notification.info({
@ -106,4 +109,5 @@ const CustomerProfile = (() => {
) )
} }
}) })
export default CustomerProfile export default CustomerProfile

@ -16,36 +16,12 @@ const { Title } = Typography
function DesktopApp() { function DesktopApp() {
const navigate = useNavigate()
const { colorPrimary, borderRadius } = useThemeContext() const { colorPrimary, borderRadius } = useThemeContext()
const loginUser = useAuthStore(state => state.loginUser) const loginUser = useAuthStore(state => state.loginUser)
const href = useHref() const href = useHref()
useEffect(() => { const totalNotify = useConversationStore((state) => state.totalNotify)
// /p...
if ((loginUser.userId === -1) && (href.indexOf('/p/') === -1)) {
navigate('/p/dingding/qrcode');
}
}, [href])
const totalNotify = useConversationStore((state) => state.totalNotify);
// useEffect(() => {
// if (loginUser.userId > 0) {
// useConversationStore.getState().connectWebsocket(loginUser.userId);
// useConversationStore.getState().fetchInitialData(loginUser.userId); // userIdStr
// }
// return () => {
// useConversationStore.getState().disconnectWebsocket();
// }
// }, [])
useEffect(() => {
Notification.requestPermission();
return () => {};
}, [])
let defaultPath = '/order/follow' let defaultPath = '/order/follow'
@ -63,19 +39,19 @@ function DesktopApp() {
/** /**
* 标签页标题闪烁 * 标签页标题闪烁
*/ */
// const [isTitleVisible, setIsTitleVisible] = useState(true); const [isTitleVisible, setIsTitleVisible] = useState(true);
// useEffect(() => { useEffect(() => {
// let interval; let interval;
// if (totalNotify > 0) { if (totalNotify > 0) {
// interval = setInterval(() => { interval = setInterval(() => {
// document.title = isTitleVisible ? `🔔🔥💬${totalNotify}` : '______________'; document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '______________';
// setIsTitleVisible(!isTitleVisible); setIsTitleVisible(!isTitleVisible);
// }, 500); }, 500);
// } else { } else {
// document.title = ''; document.title = '聊天式销售平台';
// } }
// return () => clearInterval(interval); return () => clearInterval(interval);
// }, [totalNotify, isTitleVisible]); }, [totalNotify, isTitleVisible]);
return ( return (
<Layout> <Layout>

@ -1,16 +1,23 @@
import { Conditional } from '@/components/Conditional'
import useAuthStore from '@/stores/AuthStore'
import useFormStore from '@/stores/FormStore'
import useOrderStore from '@/stores/OrderStore'
import { copy, isNotEmpty } from '@/utils/commons'
import { InfoCircleTwoTone, MailTwoTone, MessageTwoTone, PhoneTwoTone, WhatsAppOutlined } from '@ant-design/icons'
import { import {
App, Badge, Button, DatePicker, Tabs, Flex, Form, Input, Empty, App, Badge, Button,
Radio, Row, Col, Select, Space, Switch, Table, Tag, Tooltip Col,
DatePicker,
Empty,
Flex, Form, Input,
Radio, Row,
Select, Space, Switch, Table,
Tabs,
Tag, Tooltip
} from 'antd' } from 'antd'
import { InfoCircleTwoTone, MessageTwoTone, PhoneTwoTone, MailTwoTone, WhatsAppOutlined } from '@ant-design/icons' import dayjs from 'dayjs'
import { memo, useCallback, useEffect, useState } from 'react' import { memo, useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import dayjs from 'dayjs'
import { Conditional } from '@/components/Conditional'
import useOrderStore from '@/stores/OrderStore'
import useAuthStore from '@/stores/AuthStore'
import { copy, isNotEmpty } from '@/utils/commons'
import useFormStore from '@/stores/FormStore';
import { useShallow } from 'zustand/react/shallow' import { useShallow } from 'zustand/react/shallow'
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
@ -156,10 +163,20 @@ function OrderGroupTable({ formValues }) {
dataIndex: 'COLI_ID', dataIndex: 'COLI_ID',
width: 222, width: 222,
render: (text, record) => { render: (text, record) => {
if (record.COLI_LineGrade === 240003) return <Space>{text}<Tag color='red'>重点</Tag></Space> let tagIcon = ''
else if (record.COLI_LineGrade === 240002) return <Space>{text}<Tag color='green'>次重点</Tag></Space>
else if (record.COLI_LineGrade === 240001) return <Space>{text}<Tag color='blue'>一般</Tag></Space> if (record.COLI_LineGrade === 240003) tagIcon = <Tag color='red'>重点</Tag>
else return <Space>{text}</Space> else if (record.COLI_LineGrade === 240002) tagIcon = <Tag color='green'>次重点</Tag>
else if (record.COLI_LineGrade === 240001) tagIcon = <Tag color='blue'>一般</Tag>
return (
<Space>
<Link to={`/order/chat/${record.COLI_SN}`} state={record}>
{text}
</Link>
{tagIcon}
</Space>
)
} }
}, },
{ {
@ -171,9 +188,7 @@ function OrderGroupTable({ formValues }) {
return ( return (
<Space> <Space>
{isNotEmpty(record.coli_guest_WhatsApp) && <WhatsAppOutlined className={['pl-1', record.last_received_time ? 'text-whatsapp' : 'text-neutral-500']} />} {isNotEmpty(record.coli_guest_WhatsApp) && <WhatsAppOutlined className={['pl-1', record.last_received_time ? 'text-whatsapp' : 'text-neutral-500']} />}
<Link to={`/order/chat/${record.COLI_SN}`} state={record} title={record.coli_guest_WhatsApp}> {text + regularText}
{text + regularText}
</Link>
<Badge <Badge
count={record.unread_msg} count={record.unread_msg}
style={{ style={{
@ -181,7 +196,7 @@ function OrderGroupTable({ formValues }) {
}} }}
/> />
</Space> </Space>
); )
} }
}, },
{ {
@ -269,9 +284,9 @@ function OrderGroupTable({ formValues }) {
function groupByParam(array, param) { function groupByParam(array, param) {
return array.reduce((result, item) => { return array.reduce((result, item) => {
(result[item[param]] = result[item[param]] || []).push(item); (result[item[param]] = result[item[param]] || []).push(item)
return result; return result
}, {}); }, {})
} }
const deptMap = new Map([ const deptMap = new Map([
@ -335,8 +350,8 @@ function OrderGroupTable({ formValues }) {
function OrderFollow() { function OrderFollow() {
const [formValues, setFormValues] = useFormStore(useShallow((state) => [state.orderFollowForm, state.setOrderFollowForm])); const [formValues, setFormValues] = useFormStore(useShallow((state) => [state.orderFollowForm, state.setOrderFollowForm]))
const [advanceChecked, toggleAdvance] = useFormStore(useShallow((state) => [state.orderFollowAdvanceChecked, state.setOrderFollowAdvanceChecked])); const [advanceChecked, toggleAdvance] = useFormStore(useShallow((state) => [state.orderFollowAdvanceChecked, state.setOrderFollowAdvanceChecked]))
const handleSubmit = useCallback((values) => { const handleSubmit = useCallback((values) => {
setFormValues({ ...values, type: 'advance' }) setFormValues({ ...values, type: 'advance' })

@ -16,7 +16,7 @@ function QRCode() {
useEffect(() => { useEffect(() => {
if (loginUser.userId > 0) { if (loginUser.userId > 0) {
navigate('/'); navigate('/')
} }
}, [href]) }, [href])

@ -1,20 +1,20 @@
import { memo, useCallback, useEffect, useRef, useState, forwardRef } from 'react'; import { memo, useCallback, useEffect, useRef, useState, forwardRef } from 'react'
import { App, Avatar, List, Layout, Input, DatePicker, Button, Spin } from 'antd'; import { App, Avatar, List, Layout, Input, DatePicker, Button, Spin } from 'antd'
import { ChatItem, MessageBox } from 'react-chat-elements'; import { ChatItem, MessageBox } from 'react-chat-elements'
import { fetchConversationsList, fetchMessages, MESSAGE_PAGE_SIZE } from '@/actions/ConversationActions'; import { fetchConversationsList, fetchMessages, MESSAGE_PAGE_SIZE } from '@/actions/ConversationActions'
import { isEmpty } from '@/utils/utils'; import { isEmpty } from '@/utils/utils'
import useFormStore from '@/stores/FormStore'; import useFormStore from '@/stores/FormStore'
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow'
import { RightOutlined } from '@ant-design/icons' import { RightOutlined } from '@ant-design/icons'
import { fetchSalesAgent, fetchCustomerList } from '@/actions/CommonActions'; import { fetchSalesAgent, fetchCustomerList } from '@/actions/CommonActions'
import SearchInput from '@/components/SearchInput'; import SearchInput from '@/components/SearchInput'
const { Sider, Content, Header, Footer } = Layout; const { Sider, Content, Header, Footer } = Layout
const { TextArea } = Input const { TextArea } = Input
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker
const data = [ const data = [
{ {
@ -62,7 +62,7 @@ function Chat() {
}) })
}}>发送</Button> }}>发送</Button>
</> </>
); )
} }
export default Chat export default Chat

@ -1,21 +1,21 @@
import { memo, useCallback, useEffect, useRef, useState, forwardRef } from 'react'; import { memo, useCallback, useEffect, useRef, useState, forwardRef } from 'react'
import { Avatar, List, Button, Input, Layout, Select, DatePicker, Form, Spin } from 'antd'; import { Avatar, List, Button, Input, Layout, Select, DatePicker, Form, Spin } from 'antd'
import { ChatItem, MessageBox } from 'react-chat-elements'; import { ChatItem, MessageBox } from 'react-chat-elements'
import { fetchConversationsList, fetchMessages, MESSAGE_PAGE_SIZE } from '@/actions/ConversationActions'; import { fetchConversationsList, fetchMessages, MESSAGE_PAGE_SIZE } from '@/actions/ConversationActions'
import { isEmpty } from '@/utils/utils'; import { isEmpty } from '@/utils/utils'
import useFormStore from '@/stores/FormStore'; import useFormStore from '@/stores/FormStore'
import { useShallow } from 'zustand/react/shallow' import { useShallow } from 'zustand/react/shallow'
import { useNavigate, useHref } from 'react-router-dom' import { useNavigate, useHref } from 'react-router-dom'
import { RightOutlined } from '@ant-design/icons' import { RightOutlined } from '@ant-design/icons'
import { fetchSalesAgent, fetchCustomerList } from '@/actions/CommonActions'; import { fetchSalesAgent, fetchCustomerList } from '@/actions/CommonActions'
import SearchInput from '@/components/SearchInput'; import SearchInput from '@/components/SearchInput'
const { Sider, Content, Header, Footer } = Layout; const { Sider, Content, Header, Footer } = Layout
const { Search } = Input; const { Search } = Input
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker
const data = [ const data = [
{ {
@ -84,7 +84,7 @@ function Login() {
)} )}
/> />
</> </>
); )
} }
export default Login export default Login

Loading…
Cancel
Save