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

dev/chat
Lei OT 1 year ago
commit 032a9263d5

@ -3,8 +3,9 @@ import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider, useNavigate } from 'react-router-dom' import { createBrowserRouter, RouterProvider, useNavigate } from 'react-router-dom'
import { ThemeContext } from '@/stores/ThemeContext' import { ThemeContext } from '@/stores/ThemeContext'
import AuthApp from '@/views/AuthApp' import AuthApp from '@/views/AuthApp'
import Standlone from '@/views/Standlone' import DesktopApp from '@/views/DesktopApp'
import MobileApp from '@/views/MobileApp' import MobileApp from '@/views/MobileApp'
import Standlone from '@/views/Standlone'
import OrderFollow from '@/views/OrderFollow' import OrderFollow from '@/views/OrderFollow'
import ChatHistory from '@/views/ChatHistory' import ChatHistory from '@/views/ChatHistory'
import SalesManagement from '@/views/SalesManagement' import SalesManagement from '@/views/SalesManagement'
@ -27,13 +28,26 @@ const router = createBrowserRouter([
element: <AuthApp />, element: <AuthApp />,
errorElement: <ErrorPage />, errorElement: <ErrorPage />,
children: [ children: [
{ index: true, element: <OrderFollow /> }, {
{ path: 'order/follow', element: <OrderFollow /> }, element: <DesktopApp />,
{ path: 'chat/history', element: <ChatHistory /> }, children: [
{ path: 'sales/management', element: <SalesManagement /> }, { index: true, element: <OrderFollow /> },
{ path: 'order/chat/:order_sn', element: <Conversations /> }, { path: 'order/follow', element: <OrderFollow /> },
{ path: 'order/chat', element: <Conversations /> }, { path: 'chat/history', element: <ChatHistory /> },
{ path: 'account/profile', element: <AccountProfile /> }, { path: 'sales/management', element: <SalesManagement /> },
{ path: 'order/chat/:order_sn', element: <Conversations /> },
{ path: 'order/chat', element: <Conversations /> },
{ path: 'account/profile', element: <AccountProfile /> },
]
},
{
path: 'm',
element: <MobileApp />,
children: [
{ path: 'conversation', element: <MobileConversation /> },
{ path: 'chat', element: <MobileChat /> },
]
},
], ],
}, },
{ {
@ -45,14 +59,6 @@ const router = createBrowserRouter([
{ path: 'mobile-login', element: <MobileLogin />}, { path: 'mobile-login', element: <MobileLogin />},
], ],
}, },
{
path: '/m',
element: <MobileApp />,
children: [
{ path: 'conversation', element: <MobileConversation /> },
{ path: 'chat', element: <MobileChat /> },
],
},
]) ])
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(

@ -1,11 +1,9 @@
import { create } from 'zustand' import { create } from 'zustand'
import { fetchJSON } from '@/utils/request' import { fetchJSON } from '@/utils/request'
import { isNotEmpty } from '@/utils/commons' import { isEmpty, isNotEmpty } from '@/utils/commons'
const useAuthStore = create((set, get) => ({ const useAuthStore = create((set, get) => ({
// GLOBAL_SALES_LOGIN_USER
// {"userId":"383","userIdStr":"383,609","username":"廖一军","avatarUrl":"https://api.dicebear.com/7.x/miniavs/svg?seed=1","mobile":"+86-18777396951","email":"lyj@hainatravel.com","openId":"iioljiPmZ4RPoOYpkFiSn7IKAiEiE","accountList":[{"OPI_SN":383,"OPI_Code":"LYJ","OPI_NameCN":"廖一军","OPI_DEI_SN":7,"OPI_NameEN":"Jimmy Liow"},{"OPI_SN":609,"OPI_Code":"LYJAH","OPI_NameCN":"廖一军ah","OPI_DEI_SN":28,"OPI_NameEN":"Jimmy Liow"}]}
loginUser: { loginUser: {
userId: -1, userId: -1,
userIdStr: '-1', userIdStr: '-1',
@ -77,7 +75,12 @@ const useAuthStore = create((set, get) => ({
}, },
loadUserSession: () => { loadUserSession: () => {
const sessionData = window.sessionStorage.getItem('GLOBAL_SALES_LOGIN_USER') let sessionData = window.sessionStorage.getItem('GLOBAL_SALES_LOGIN_USER')
if (import.meta.env.DEV && isEmpty(sessionData)) {
sessionData = window.localStorage.getItem('GLOBAL_SALES_LOGIN_USER')
}
if (sessionData !== null) { if (sessionData !== null) {
set(() => ({ set(() => ({
loginUser: JSON.parse(sessionData) loginUser: JSON.parse(sessionData)

@ -2,20 +2,15 @@ import ErrorBoundary from '@/components/ErrorBoundary'
import useAuthStore from '@/stores/AuthStore' import useAuthStore from '@/stores/AuthStore'
import useConversationStore from '@/stores/ConversationStore' import useConversationStore from '@/stores/ConversationStore'
import { useThemeContext } from '@/stores/ThemeContext' import { useThemeContext } from '@/stores/ThemeContext'
import { DownOutlined } from '@ant-design/icons' import { App as AntApp, ConfigProvider, Empty, Layout, Typography, theme } from 'antd'
import { App as AntApp, Avatar, Col, ConfigProvider, Dropdown, Empty, Layout, Menu, Row, Space, Typography, theme, Badge } from 'antd'
import zhLocale from 'antd/locale/zh_CN' import zhLocale from 'antd/locale/zh_CN'
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Link, NavLink, Outlet, useHref, useNavigate } from 'react-router-dom' import { Outlet, useHref, useNavigate } from 'react-router-dom'
import '@/assets/App.css' import '@/assets/App.css'
import AppLogo from '@/assets/logo-gh.png'
import 'react-chat-elements/dist/main.css' import 'react-chat-elements/dist/main.css'
const { Header, Footer, Content } = Layout
const { Title } = Typography
function AuthApp() { function AuthApp() {
const navigate = useNavigate() const navigate = useNavigate()
@ -92,76 +87,8 @@ function AuthApp() {
renderEmpty={() => <Empty description={false} />} renderEmpty={() => <Empty description={false} />}
> >
<AntApp> <AntApp>
<ErrorBoundary> <ErrorBoundary>
<Layout> <Outlet />
<Header className='header' style={{ position: 'sticky', top: 0, zIndex: 2, width: '100%', background: 'white' }}>
<Row gutter={{ md: 24 }} align='middle'>
<Col flex='300px'>
<NavLink to='/'>
<img src={AppLogo} className='logo' alt='App logo' />
</NavLink>
<Title level={3}>
聊天式销售平台
</Title>
</Col>
<Col span={10}>
<Menu
mode='horizontal'
selectedKeys={[defaultPath]}
items={[
{ key: '/order/follow', label: <Link to='/order/follow'>订单跟踪</Link> },
{ key: '/order/chat', label: <Link to='/order/chat'>在线聊天
<Badge
count={totalNotify}
style={{
backgroundColor: '#52c41a',
}}
/></Link> },
{ key: '/chat/history', label: <Link to='/chat/history'>聊天记录</Link> },
]}
/>
</Col>
<Col flex='auto' style={{ color: 'white', marginBottom: '0', display: 'flex', justifyContent: 'end' }}>
<Dropdown
menu={{
items: [
{
label: <Link to='/account/profile'>个人资料</Link>,
key: '1',
},
{
type: 'divider',
},
{
label: <Link to='/p/dingding/qrcode?out'>退出</Link>,
key: '3',
},
]
}}
trigger={['click']}
>
<a onClick={(e) => e.preventDefault()} style={{ color: colorPrimary }}>
<Space><Avatar
src={loginUser.avatarUrl}>{loginUser?.username?.substring(1)}</Avatar>{loginUser.username}<DownOutlined /></Space>
</a>
</Dropdown>
</Col>
</Row>
</Header>
<Layout>
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: colorBgContainer,
}}>
<Outlet />
</Content>
</Layout>
<Footer>桂林海纳国际旅行社有限公司</Footer>
</Layout>
</ErrorBoundary> </ErrorBoundary>
</AntApp> </AntApp>
</ConfigProvider> </ConfigProvider>

@ -60,7 +60,8 @@ const CustomerProfile = (() => {
{ value: 4, label: '等待付订金' }, { value: 4, label: '等待付订金' },
{ value: 5, label: '成行' }, { value: 5, label: '成行' },
{ value: 6, label: '丢失' }, { value: 6, label: '丢失' },
{ value: 7, label: '取消' } { value: 7, label: '取消' },
{ value: 8, label: '未报价' }
]} ]}
/> />
]} ]}

@ -0,0 +1,153 @@
import useAuthStore from '@/stores/AuthStore'
import useConversationStore from '@/stores/ConversationStore'
import { useThemeContext } from '@/stores/ThemeContext'
import { DownOutlined } from '@ant-design/icons'
import { Avatar, Col, Dropdown, Layout, Menu, Row, Space, Typography, theme, Badge } from 'antd'
import 'dayjs/locale/zh-cn'
import { useEffect, useState } from 'react'
import { Link, NavLink, Outlet, useHref, useNavigate } from 'react-router-dom'
import '@/assets/App.css'
import AppLogo from '@/assets/logo-gh.png'
import 'react-chat-elements/dist/main.css'
const { Header, Footer, Content } = Layout
const { Title } = Typography
function DesktopApp() {
const navigate = useNavigate()
const { colorPrimary, borderRadius } = useThemeContext()
const loginUser = useAuthStore(state => state.loginUser)
const href = useHref()
useEffect(() => {
// /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'
if (href !== '/') {
const splitPath = href.split('/')
if (splitPath.length > 2) {
defaultPath = '/' + splitPath[1] + '/' + splitPath[2]
}
}
const {
token: { colorBgContainer },
} = 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 (
<Layout>
<Header className='header' style={{ position: 'sticky', top: 0, zIndex: 2, width: '100%', background: 'white' }}>
<Row gutter={{ md: 24 }} align='middle'>
<Col flex='300px'>
<NavLink to='/'>
<img src={AppLogo} className='logo' alt='App logo' />
</NavLink>
<Title level={3}>
聊天式销售平台
</Title>
</Col>
<Col span={10}>
<Menu
mode='horizontal'
selectedKeys={[defaultPath]}
items={[
{ key: '/order/follow', label: <Link to='/order/follow'>订单跟踪</Link> },
{ key: '/order/chat', label: <Link to='/order/chat'>在线聊天
<Badge
count={totalNotify}
style={{
backgroundColor: '#52c41a',
}}
/></Link> },
{ key: '/chat/history', label: <Link to='/chat/history'>聊天记录</Link> },
]}
/>
</Col>
<Col flex='auto' style={{ color: 'white', marginBottom: '0', display: 'flex', justifyContent: 'end' }}>
<Dropdown
menu={{
items: [
{
label: <Link to='/account/profile'>个人资料</Link>,
key: '1',
},
{
type: 'divider',
},
{
label: <Link to='/p/dingding/qrcode?out'>退出</Link>,
key: '3',
},
]
}}
trigger={['click']}
>
<a onClick={(e) => e.preventDefault()} style={{ color: colorPrimary }}>
<Space><Avatar
src={loginUser.avatarUrl}>{loginUser?.username?.substring(1)}</Avatar>{loginUser.username}<DownOutlined /></Space>
</a>
</Dropdown>
</Col>
</Row>
</Header>
<Layout>
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: colorBgContainer,
}}>
<Outlet />
</Content>
</Layout>
<Footer>桂林海纳国际旅行社有限公司</Footer>
</Layout>
)
}
export default DesktopApp

@ -2,7 +2,7 @@ import '@/assets/App.css'
import AppLogo from '@/assets/logo-gh.png' import AppLogo from '@/assets/logo-gh.png'
import { useThemeContext } from '@/stores/ThemeContext' import { useThemeContext } from '@/stores/ThemeContext'
import useAuthStore from '@/stores/AuthStore' import useAuthStore from '@/stores/AuthStore'
import { App as AntApp, Col, ConfigProvider, Empty, Layout, Row, Typography, theme, Space, Avatar } from 'antd' import { Col, Layout, Row, Typography, theme, Space, Avatar } from 'antd'
import { DownOutlined } from '@ant-design/icons' import { DownOutlined } from '@ant-design/icons'
import { NavLink, Outlet } from 'react-router-dom' import { NavLink, Outlet } from 'react-router-dom'
const { Header, Footer, Content } = Layout const { Header, Footer, Content } = Layout
@ -17,51 +17,32 @@ function MobileApp() {
token: { colorBgContainer }, token: { colorBgContainer },
} = theme.useToken() } = theme.useToken()
function renderLayout() { return (
return ( <Layout>
<Header className='header' style={{ position: 'sticky', top: 0, zIndex: 1, width: '100%', background: 'white' }}>
<Row gutter={{ md: 24 }} align='middle'>
<Col flex="auto" style={{ color: "white", marginBottom: "0", display: "flex", justifyContent: "center" }}>
<NavLink to='/'>
<img src={AppLogo} className='logo' alt='App logo' />
</NavLink>
<Space><Avatar
src={loginUser.avatarUrl}>{loginUser?.username?.substring(1)}</Avatar><span style={{ color: colorPrimary }}>{loginUser.username}</span><DownOutlined /></Space>
</Col>
</Row>
</Header>
<Layout> <Layout>
<Header className='header' style={{ position: 'sticky', top: 0, zIndex: 1, width: '100%', background: 'white' }}> <Content
<Row gutter={{ md: 24 }} align='middle'> style={{
<Col flex="auto" style={{ color: "white", marginBottom: "0", display: "flex", justifyContent: "center" }}> padding: 0,
<NavLink to='/'> margin: 0,
<img src={AppLogo} className='logo' alt='App logo' /> minHeight: 200,
</NavLink> background: colorBgContainer,
<Space><Avatar }}>
src={loginUser.avatarUrl}>{loginUser?.username?.substring(1)}</Avatar><span style={{ color: colorPrimary }}>{loginUser.username}</span><DownOutlined /></Space> <Outlet />
</Col> </Content>
</Row>
</Header>
<Layout>
<Content
style={{
padding: 0,
margin: 0,
minHeight: 200,
background: colorBgContainer,
}}>
<Outlet />
</Content>
</Layout>
<Footer>桂林海纳国际旅行社有限公司</Footer>
</Layout> </Layout>
) <Footer>桂林海纳国际旅行社有限公司</Footer>
} </Layout>
return (
<ConfigProvider
theme={{
token: {
colorPrimary: colorPrimary,
borderRadius: borderRadius
},
algorithm: theme.defaultAlgorithm,
}}
renderEmpty={() => <Empty description={false} />}
>
<AntApp>
{renderLayout()}
</AntApp>
</ConfigProvider>
) )
} }

@ -367,7 +367,7 @@ function OrderFollow() {
disabled={advanceChecked} disabled={advanceChecked}
/> />
<Switch checkedChildren='高级查询' unCheckedChildren='高级查询' <Switch checkedChildren='高级查询' unCheckedChildren='高级查询'
defaultChecked={advanceChecked} checked={advanceChecked} checked={advanceChecked}
onChange={() => { toggleAdvance(!advanceChecked) }} /> onChange={() => { toggleAdvance(!advanceChecked) }} />
</Flex> </Flex>
<Conditional condition={advanceChecked} whenTrue={<AdvanceSearchForm onSubmit={handleSubmit} initialValues={formValues} />} <Conditional condition={advanceChecked} whenTrue={<AdvanceSearchForm onSubmit={handleSubmit} initialValues={formValues} />}

@ -1,5 +1,4 @@
import { memo, useCallback, useEffect, useRef, useState, forwardRef } from 'react' import { Flex, Result, Spin } from 'antd'
import { App, Avatar, List, Layout, Input, DatePicker, Flex, Result, Spin } from 'antd'
function Chat() { function Chat() {
@ -16,7 +15,7 @@ function Chat() {
]} ]}
/> />
</Flex> </Flex>
); )
} }
export default Chat export default Chat

Loading…
Cancel
Save