开发: push
This commit is contained in:
@@ -59,7 +59,7 @@ export default defineConfig({
|
||||
// proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://192.168.1.219:8183/',
|
||||
target: 'http://192.168.1.138:8183/',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '' },
|
||||
},
|
||||
|
@@ -55,11 +55,17 @@ export default [
|
||||
component: './DepartmentsList',
|
||||
},
|
||||
{
|
||||
name: '聊天记录',
|
||||
name: '成员聊天',
|
||||
icon: 'table',
|
||||
path: '/departments/page/list2',
|
||||
component: './ChatLogs',
|
||||
},
|
||||
{
|
||||
name: '客户聊天',
|
||||
icon: 'table',
|
||||
path: '/departments/page/list3',
|
||||
component: './ChatLogs',
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import { LinkOutlined, VerticalAlignTopOutlined } from '@ant-design/icons';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
||||
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
|
||||
import { history, Link } from '@umijs/max';
|
||||
import { App, FloatButton } from 'antd';
|
||||
import { App } from 'antd';
|
||||
import React from 'react';
|
||||
import defaultSettings from '../config/defaultSettings';
|
||||
import { AvatarDropdown, AvatarName } from './components/RightContent/AvatarDropdown';
|
||||
@@ -116,13 +116,13 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<FloatButton
|
||||
{/* <FloatButton
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
></FloatButton>
|
||||
></FloatButton> */}
|
||||
{/* <SettingDrawer
|
||||
disableUrlParams
|
||||
enableDarkTheme
|
||||
|
@@ -1,271 +0,0 @@
|
||||
import { PlayCircleOutlined } from '@ant-design/icons';
|
||||
import styles from './index.module.scss';
|
||||
export const ChatBar: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content}>
|
||||
通过 message.useMessage 创建支持读取 context 的
|
||||
contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
|
||||
静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content}>
|
||||
通过 message.useMessage 创建支持读取 context 的
|
||||
contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
|
||||
静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IChatTimeProps {
|
||||
msgtime: number;
|
||||
}
|
||||
|
||||
export const ChatTime: React.FC<IChatTimeProps> = (props) => {
|
||||
function padWith(value: string | number) {
|
||||
return `${value}`.padStart(2, '0');
|
||||
}
|
||||
|
||||
const weekStr = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
|
||||
const formatTime = () => {
|
||||
const now = new Date();
|
||||
const msgtime = new Date(props.msgtime);
|
||||
console.log(msgtime.toLocaleString());
|
||||
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const dayDiff =
|
||||
(Date.parse(
|
||||
`${now.getFullYear()}-${padWith(now.getMonth() + 1)}-${padWith(now.getDate())} 00:00:00`,
|
||||
) -
|
||||
props.msgtime) /
|
||||
(24 * 60 * 60 * 1000) +
|
||||
1;
|
||||
|
||||
if (now.getFullYear() == msgtime.getFullYear()) {
|
||||
if (
|
||||
now.getFullYear() == msgtime.getFullYear() &&
|
||||
now.getMonth() == msgtime.getMonth() &&
|
||||
now.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 当天
|
||||
return `${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (
|
||||
yesterday.getFullYear() == msgtime.getFullYear() &&
|
||||
yesterday.getMonth() == msgtime.getMonth() &&
|
||||
yesterday.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 昨天
|
||||
return `昨天 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (dayDiff < 7) {
|
||||
// 星期
|
||||
return `星期${weekStr[msgtime.getDay()]} ${padWith(msgtime.getHours())}:${padWith(
|
||||
msgtime.getMinutes(),
|
||||
)}`;
|
||||
} else {
|
||||
// 超过7天
|
||||
return `${padWith(msgtime.getMonth() + 1)}月${padWith(msgtime.getDate())}日 ${padWith(
|
||||
msgtime.getHours(),
|
||||
)}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
} else {
|
||||
// 跨年
|
||||
return `${msgtime.getFullYear()}年${padWith(msgtime.getMonth() + 1)}月${padWith(
|
||||
msgtime.getDate(),
|
||||
)}日 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{formatTime()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChatRevoke: React.FC = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
"xxxxx" 撤回了一条消息
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChatAgreeOrNot: React.FC = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
对方[不]同意存档会话内容,你将无法继续提供服务
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 语音
|
||||
export const ChatVoice = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content} style={{ cursor: 'pointer' }}>
|
||||
<span style={{ lineHeight: 1, marginRight: 4 }}>2"</span>
|
||||
<svg viewBox="0 0 1024 1024" width="18" height="18">
|
||||
<path
|
||||
d="M913.568627 518.334145C913.568627 483.422393 894.958532 451.162604 864.748584 433.706729 834.538616 416.250852 797.318425 416.250852 767.108477 433.706729 736.89851 451.162604 718.288414 483.422393 718.288414 518.334145 718.288414 553.245895 736.89851 585.505684 767.108477 602.961559 797.318425 620.417436 834.538616 620.417436 864.748584 602.961559 894.958532 585.505684 913.568627 553.245895 913.568627 518.334145L913.568627 518.334145ZM581.566143 215.288946C581.566143 215.288946 593.269057 203.098427 620.669932 203.098427 650.6546 203.098427 674.950626 227.414165 674.950626 257.423171 674.950626 290.452615 648.917554 305.250663 636.758739 309.031723 568.451875 353.599725 523.116685 430.436655 523.116685 518.116824 523.116685 605.970743 568.603889 682.938067 637.149606 727.440872 650.915177 731.852057 674.950626 743.064634 674.950626 778.940765 674.950626 808.949772 650.6546 833.265507 620.669932 833.265507 593.269057 833.265507 581.566143 821.07499 581.566143 821.07499 481.233659 757.536806 414.403283 645.801687 414.403283 518.181916 414.403283 390.562145 481.233659 278.805502 581.566143 215.288946L581.566143 215.288946ZM348.376225 14.135304C348.376225 14.135304 365.941399 7.529394 392.690949 7.529394 428.668185 7.529394 457.82781 36.73442 457.82781 72.719107 457.82781 98.186537 406.282822 128.195544 388.54391 137.474179 297.265513 229.695819 240.704994 356.381112 240.704994 496.452082 240.704994 650.321426 309.055287 787.849957 416.661363 881.418878 440.740242 890.980031 457.82781 914.426591 457.82781 941.914889 457.82781 977.899576 428.668185 1007.104602 392.690949 1007.104602 363.314276 1007.104602 345.336511 998.13011 345.336511 998.13011 202.12236 882.635664 110.431373 705.732666 110.431373 507.316947 110.431373 307.532423 203.446734 129.564556 348.376225 14.135304L348.376225 14.135304Z"
|
||||
fill="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// 视频
|
||||
export const ChatVideo = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.video}>
|
||||
<PlayCircleOutlined style={{ color: '#fff', fontSize: 40 }} />
|
||||
<span className={styles.videoTime}>0:46</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 名片
|
||||
export const ChatCard = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div className={styles.chatAvatar} style={{ marginRight: 12 }}>
|
||||
<img
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.corpname}>腾讯</div>
|
||||
</div>
|
||||
<div>corpname</div>
|
||||
<div style={{ color: '#999' }}>corpname2</div>
|
||||
<div style={{ color: '#999', borderTop: '1px solid #ddd' }}>个人名片</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 表情
|
||||
export const ChatEmotion = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// 文件
|
||||
export const ChatFile = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
36
src/pages/ChatLogs/ChatLogsType.ts
Normal file
36
src/pages/ChatLogs/ChatLogsType.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export interface IStaffsItem {
|
||||
user_id: string | number;
|
||||
name: string;
|
||||
telephone: string;
|
||||
dep_name: string[];
|
||||
position: string;
|
||||
}
|
||||
|
||||
export interface ICustFollow {
|
||||
cust_id: string;
|
||||
cust_info: {
|
||||
avatar: string;
|
||||
cust_id: string;
|
||||
gender: number;
|
||||
name: string;
|
||||
sync_time: string;
|
||||
type: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IChat {
|
||||
action: string;
|
||||
content: string;
|
||||
msg_from: string;
|
||||
msg_id: string;
|
||||
msg_time: string;
|
||||
msg_to_list: string;
|
||||
msg_type: string;
|
||||
room_id: string;
|
||||
}
|
||||
export interface IChatItem {
|
||||
from?: IStaffsItem;
|
||||
to?: ICustFollow;
|
||||
group?: any;
|
||||
chat?: IChat;
|
||||
}
|
24
src/pages/ChatLogs/components/ChatAgreeOrNot.tsx
Normal file
24
src/pages/ChatLogs/components/ChatAgreeOrNot.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
interface IProps {
|
||||
msg_type: string;
|
||||
}
|
||||
export const ChatAgreeOrNot: React.FC<IProps> = (props) => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 12 }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
对方{props.msg_type == 'agree' ? '' : '不'}同意存档会话内容,你将无法继续提供服务
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
46
src/pages/ChatLogs/components/ChatBar.tsx
Normal file
46
src/pages/ChatLogs/components/ChatBar.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import { ChatAgreeOrNot } from './ChatAgreeOrNot';
|
||||
import { ChatCard } from './ChatCard';
|
||||
import { ChatEmotion } from './ChatEmotion';
|
||||
import { ChatFile } from './ChatFile';
|
||||
import { ChatImage } from './ChatImage';
|
||||
import { ChatLink } from './ChatLink';
|
||||
import { ChatLocation } from './ChatLocation';
|
||||
import { ChatRecord } from './ChatRecord';
|
||||
import { ChatRedpacket } from './ChatRedpacket';
|
||||
import { ChatRevoke } from './ChatRevoke';
|
||||
import { ChatText } from './ChatText';
|
||||
import { ChatVideo } from './ChatVideo';
|
||||
import { ChatVoice } from './ChatVoice';
|
||||
|
||||
export const ChatBar: React.FC<IChatItem> = (props) => {
|
||||
const { from, to, chat } = props;
|
||||
if (chat?.msg_type == 'text') {
|
||||
return <ChatText from={from} to={to} chat={chat}></ChatText>;
|
||||
} else if (chat?.msg_type == 'file') {
|
||||
return <ChatFile from={from} to={to} chat={chat}></ChatFile>;
|
||||
} else if (chat?.msg_type == 'emotion') {
|
||||
return <ChatEmotion from={from} to={to} chat={chat}></ChatEmotion>;
|
||||
} else if (chat?.msg_type == 'card') {
|
||||
return <ChatCard from={from} to={to} chat={chat}></ChatCard>;
|
||||
} else if (chat?.msg_type == 'external_redpacket') {
|
||||
return <ChatRedpacket from={from} to={to} chat={chat}></ChatRedpacket>;
|
||||
} else if (chat?.msg_type == 'image') {
|
||||
return <ChatImage from={from} to={to} chat={chat}></ChatImage>;
|
||||
} else if (chat?.msg_type == 'link') {
|
||||
return <ChatLink from={from} to={to} chat={chat}></ChatLink>;
|
||||
} else if (chat?.msg_type == 'location') {
|
||||
return <ChatLocation from={from} to={to} chat={chat}></ChatLocation>;
|
||||
} else if (chat?.msg_type == 'video') {
|
||||
return <ChatVideo from={from} to={to} chat={chat}></ChatVideo>;
|
||||
} else if (chat?.msg_type == 'voice') {
|
||||
return <ChatVoice from={from} to={to} chat={chat}></ChatVoice>;
|
||||
} else if (chat?.msg_type == 'chatrecord') {
|
||||
return <ChatRecord from={from} to={to} chat={chat}></ChatRecord>;
|
||||
} else if (chat?.msg_type == 'revoke') {
|
||||
return <ChatRevoke></ChatRevoke>;
|
||||
} else if (chat?.msg_type == 'agree' || chat?.msg_type == 'disagree') {
|
||||
return <ChatAgreeOrNot msg_type={chat?.msg_type}></ChatAgreeOrNot>;
|
||||
}
|
||||
return <div></div>;
|
||||
};
|
57
src/pages/ChatLogs/components/ChatCard.tsx
Normal file
57
src/pages/ChatLogs/components/ChatCard.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
export const ChatCard: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
// <div style={{ display: 'flex' }}>
|
||||
// <div className={styles.chatAvatar} style={{ marginRight: 12 }}>
|
||||
// <img
|
||||
// src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
|
||||
// alt=""
|
||||
// />
|
||||
// </div>
|
||||
// <div className={styles.corpname}>腾讯</div>
|
||||
// </div>
|
||||
// <div>corpname</div>
|
||||
// <div style={{ color: '#999' }}>corpname2</div>
|
||||
// <div style={{ color: '#999', borderTop: '1px solid #ddd' }}>个人名片</div>
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
return (
|
||||
<div style={{ display: 'flex', color: '#000', flexDirection: 'column', width: 200 }}>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{msg.corpname}</div>
|
||||
<div style={{ color: '#999', borderTop: '1px solid #ddd' }}>名片</div>
|
||||
</div>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
56
src/pages/ChatLogs/components/ChatEmotion.tsx
Normal file
56
src/pages/ChatLogs/components/ChatEmotion.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
// 表情
|
||||
export const ChatEmotion: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
color: '#000',
|
||||
width: 180,
|
||||
height: 150,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`/api/${msg.path}`}
|
||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} catch (_e) {
|
||||
return <div>{props.chat?.content}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
52
src/pages/ChatLogs/components/ChatFile.tsx
Normal file
52
src/pages/ChatLogs/components/ChatFile.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { FileTextFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatFile: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
return (
|
||||
<a
|
||||
style={{ display: 'flex', color: '#000' }}
|
||||
download={msg.file_name}
|
||||
href={`/api/${msg.path}`}
|
||||
>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{msg.file_name}</div>
|
||||
<FileTextFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</a>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
<FileTextFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
58
src/pages/ChatLogs/components/ChatImage.tsx
Normal file
58
src/pages/ChatLogs/components/ChatImage.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatImage: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
color: '#000',
|
||||
width: 180,
|
||||
height: 150,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`/api/${msg.path}`}
|
||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
57
src/pages/ChatLogs/components/ChatLink.tsx
Normal file
57
src/pages/ChatLogs/components/ChatLink.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { IeSquareFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatLink: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', color: '#000' }}>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>
|
||||
<div>{msg.title}</div>
|
||||
<div>{msg.description}</div>
|
||||
<a href={msg.link_url} target="_blank">
|
||||
{msg.link_url}
|
||||
</a>
|
||||
</div>
|
||||
{msg.image_url ? (
|
||||
<img src={msg.image_url} style={{ width: 40, height: 40 }} alt="" />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
<IeSquareFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
56
src/pages/ChatLogs/components/ChatLocation.tsx
Normal file
56
src/pages/ChatLogs/components/ChatLocation.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { EnvironmentFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatLocation: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
|
||||
return (
|
||||
<a
|
||||
style={{ display: 'flex', color: '#000' }}
|
||||
download={msg.file_name}
|
||||
href={`/api/${msg.path}`}
|
||||
>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>
|
||||
<div>{msg.title}</div>
|
||||
<div>{msg.address}</div>
|
||||
</div>
|
||||
<EnvironmentFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</a>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
<EnvironmentFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
52
src/pages/ChatLogs/components/ChatRecord.tsx
Normal file
52
src/pages/ChatLogs/components/ChatRecord.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { FileTextFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatRecord: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
return (
|
||||
<a
|
||||
style={{ display: 'flex', color: '#000' }}
|
||||
download={msg.file_name}
|
||||
href={`/api/${msg.path}`}
|
||||
>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{msg.file_name}</div>
|
||||
<FileTextFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</a>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
<FileTextFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
63
src/pages/ChatLogs/components/ChatRedpacket.tsx
Normal file
63
src/pages/ChatLogs/components/ChatRedpacket.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { RedEnvelopeFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatRedpacket: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<RedEnvelopeFilled
|
||||
style={{ fontSize: 40, color: 'red', flexShrink: 0, paddingRight: 8 }}
|
||||
/>
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all' }}>{msg.wish}</div>
|
||||
<div style={{ wordBreak: 'break-all' }}>
|
||||
{msg.totalcnt}个,{(msg.totalamount / 100).toFixed(2)}元
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ borderTop: '1px solid #ddd', marginTop: 12 }}>红包</div>
|
||||
</div>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<RedEnvelopeFilled
|
||||
style={{ fontSize: 40, color: 'red', flexShrink: 0, paddingRight: 8 }}
|
||||
/>
|
||||
<div style={{ wordBreak: 'break-all' }}>{props.chat?.content}</div>
|
||||
</div>
|
||||
<div style={{ borderTop: '1px solid #ddd', marginTop: 12 }}>红包</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
21
src/pages/ChatLogs/components/ChatRevoke.tsx
Normal file
21
src/pages/ChatLogs/components/ChatRevoke.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
export const ChatRevoke: React.FC = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 12 }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
撤回了一条消息
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
28
src/pages/ChatLogs/components/ChatText.tsx
Normal file
28
src/pages/ChatLogs/components/ChatText.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatText: React.FC<IChatItem> = (props) => {
|
||||
return (
|
||||
<div>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{props.chat?.content}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{props.chat?.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
79
src/pages/ChatLogs/components/ChatTime.tsx
Normal file
79
src/pages/ChatLogs/components/ChatTime.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
interface IChatTimeProps {
|
||||
msgtime: string;
|
||||
}
|
||||
|
||||
export const ChatTime: React.FC<IChatTimeProps> = (props) => {
|
||||
function padWith(value: string | number) {
|
||||
return `${value}`.padStart(2, '0');
|
||||
}
|
||||
|
||||
const weekStr = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
|
||||
const formatTime = () => {
|
||||
const now = new Date();
|
||||
const msgtime = new Date(props.msgtime);
|
||||
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const dayDiff =
|
||||
(Date.parse(
|
||||
`${now.getFullYear()}-${padWith(now.getMonth() + 1)}-${padWith(now.getDate())} 00:00:00`,
|
||||
) -
|
||||
new Date(props.msgtime).getTime()) /
|
||||
(24 * 60 * 60 * 1000) +
|
||||
1;
|
||||
|
||||
if (now.getFullYear() == msgtime.getFullYear()) {
|
||||
if (
|
||||
now.getFullYear() == msgtime.getFullYear() &&
|
||||
now.getMonth() == msgtime.getMonth() &&
|
||||
now.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 当天
|
||||
return `${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (
|
||||
yesterday.getFullYear() == msgtime.getFullYear() &&
|
||||
yesterday.getMonth() == msgtime.getMonth() &&
|
||||
yesterday.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 昨天
|
||||
return `昨天 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (dayDiff < 7) {
|
||||
// 星期
|
||||
return `星期${weekStr[msgtime.getDay()]} ${padWith(msgtime.getHours())}:${padWith(
|
||||
msgtime.getMinutes(),
|
||||
)}`;
|
||||
} else {
|
||||
// 超过7天
|
||||
return `${padWith(msgtime.getMonth() + 1)}月${padWith(msgtime.getDate())}日 ${padWith(
|
||||
msgtime.getHours(),
|
||||
)}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
} else {
|
||||
// 跨年
|
||||
return `${msgtime.getFullYear()}年${padWith(msgtime.getMonth() + 1)}月${padWith(
|
||||
msgtime.getDate(),
|
||||
)}日 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{formatTime()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
60
src/pages/ChatLogs/components/ChatVideo.tsx
Normal file
60
src/pages/ChatLogs/components/ChatVideo.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { PlayCircleOutlined } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatVideo: React.FC<IChatItem> = (props) => {
|
||||
function playLen(v: number) {
|
||||
const len = Number(v) || 0;
|
||||
if (len > 60) {
|
||||
return `${Math.floor(len / 60)
|
||||
.toString()
|
||||
.padStart(2, '0')}:${(len % 60).toString().padStart(2, '0')}`;
|
||||
} else {
|
||||
return `00:${len.toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
// console.log(msg);
|
||||
return (
|
||||
<a className={styles.video} href={`/api/${msg.path}`} target="_blank">
|
||||
<PlayCircleOutlined style={{ color: '#fff', fontSize: 40 }} />
|
||||
<span className={styles.videoTime}>{playLen(msg.play_length)}</span>
|
||||
</a>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div className={styles.video}>
|
||||
<div style={{ position: 'absolute', bottom: 12, right: 12, color: '#fff' }}>数据错误</div>
|
||||
<PlayCircleOutlined style={{ fontSize: 40, color: '#fff', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
56
src/pages/ChatLogs/components/ChatVoice.tsx
Normal file
56
src/pages/ChatLogs/components/ChatVoice.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { FileTextFilled } from '@ant-design/icons';
|
||||
import { IChatItem } from '../ChatLogsType';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const ChatVoice: React.FC<IChatItem> = (props) => {
|
||||
function content() {
|
||||
try {
|
||||
const msg = JSON.parse(props.chat?.content as string);
|
||||
|
||||
return (
|
||||
<a style={{ display: 'flex', color: '#000' }} href={`/api/${msg.path}`} target="_blank">
|
||||
<span style={{ lineHeight: 1, marginRight: 4, minWidth: 100, textAlign: 'right' }}>
|
||||
{msg.play_length}"
|
||||
</span>
|
||||
<svg viewBox="0 0 1024 1024" width="18" height="18">
|
||||
<path
|
||||
d="M913.568627 518.334145C913.568627 483.422393 894.958532 451.162604 864.748584 433.706729 834.538616 416.250852 797.318425 416.250852 767.108477 433.706729 736.89851 451.162604 718.288414 483.422393 718.288414 518.334145 718.288414 553.245895 736.89851 585.505684 767.108477 602.961559 797.318425 620.417436 834.538616 620.417436 864.748584 602.961559 894.958532 585.505684 913.568627 553.245895 913.568627 518.334145L913.568627 518.334145ZM581.566143 215.288946C581.566143 215.288946 593.269057 203.098427 620.669932 203.098427 650.6546 203.098427 674.950626 227.414165 674.950626 257.423171 674.950626 290.452615 648.917554 305.250663 636.758739 309.031723 568.451875 353.599725 523.116685 430.436655 523.116685 518.116824 523.116685 605.970743 568.603889 682.938067 637.149606 727.440872 650.915177 731.852057 674.950626 743.064634 674.950626 778.940765 674.950626 808.949772 650.6546 833.265507 620.669932 833.265507 593.269057 833.265507 581.566143 821.07499 581.566143 821.07499 481.233659 757.536806 414.403283 645.801687 414.403283 518.181916 414.403283 390.562145 481.233659 278.805502 581.566143 215.288946L581.566143 215.288946ZM348.376225 14.135304C348.376225 14.135304 365.941399 7.529394 392.690949 7.529394 428.668185 7.529394 457.82781 36.73442 457.82781 72.719107 457.82781 98.186537 406.282822 128.195544 388.54391 137.474179 297.265513 229.695819 240.704994 356.381112 240.704994 496.452082 240.704994 650.321426 309.055287 787.849957 416.661363 881.418878 440.740242 890.980031 457.82781 914.426591 457.82781 941.914889 457.82781 977.899576 428.668185 1007.104602 392.690949 1007.104602 363.314276 1007.104602 345.336511 998.13011 345.336511 998.13011 202.12236 882.635664 110.431373 705.732666 110.431373 507.316947 110.431373 307.532423 203.446734 129.564556 348.376225 14.135304L348.376225 14.135304Z"
|
||||
fill="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
);
|
||||
} catch (_e) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ wordBreak: 'break-all', paddingRight: 8 }}>{props.chat?.content}</div>
|
||||
<FileTextFilled style={{ fontSize: 40, color: '#FA9D3B', flexShrink: 0 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.from?.user_id == props.chat?.msg_from ? (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.from?.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>{props.from?.name[0]}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src={props.to?.cust_info.avatar} alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>{props.to?.cust_info.name}</div>
|
||||
<div className={styles.content}>{content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
111
src/pages/ChatLogs/components/index.module.scss
Normal file
111
src/pages/ChatLogs/components/index.module.scss
Normal file
@@ -0,0 +1,111 @@
|
||||
.chatLeft {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
max-width: 60%;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.chatAvatar {
|
||||
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: cover;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 8px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
word-break: break-all;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: -6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fff;
|
||||
transform: rotate(45deg);
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
}
|
||||
|
||||
.chatRight {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.name {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
justify-content: end;
|
||||
max-width: 60%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #95ec69;
|
||||
|
||||
&::before {
|
||||
right: -6px;
|
||||
left: initial;
|
||||
background-color: #95ec69;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #89d961;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 176px;
|
||||
height: 144px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.videoTime {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
@@ -1,109 +1,114 @@
|
||||
.chatLeft {
|
||||
.box {
|
||||
display: flex;
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
max-width: 60%;
|
||||
margin-left: 16px;
|
||||
}
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.chatAvatar {
|
||||
|
||||
.personnelBox {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: cover;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
width: 251px;
|
||||
height: calc(100vh - 212px);
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 8px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
word-break: break-all;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: -6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fff;
|
||||
transform: rotate(45deg);
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
}
|
||||
|
||||
.chatRight {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
.name {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
justify-content: end;
|
||||
max-width: 60%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #95ec69;
|
||||
|
||||
&::before {
|
||||
right: -6px;
|
||||
left: initial;
|
||||
background-color: #95ec69;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #89d961;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
position: relative;
|
||||
.chatA,
|
||||
.chatB {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 176px;
|
||||
height: 144px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
justify-content: flex-start;
|
||||
height: 65px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
|
||||
.videoTime {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background-color: #d6d6d7;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #c5c4c4;
|
||||
}
|
||||
}
|
||||
|
||||
.chatB {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.chatAMsg {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
padding-left: 12px;
|
||||
|
||||
.chatAName {
|
||||
flex: 1;
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
.chatBBox {
|
||||
height: calc(100vh - 212px - 164px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.chatLogBox {
|
||||
height: calc(100vh - 212px - 66px);
|
||||
padding: 12px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.logTop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 65px;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.flolowsBox {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
transform: translateY(20px);
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s, visibility 0.3s, opacity 0.3s;
|
||||
pointer-events: none;
|
||||
|
||||
&.show {
|
||||
z-index: 1;
|
||||
transform: translateY(0px);
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
@@ -1,159 +1,306 @@
|
||||
import { SearchBarPlugin, SearchBottonsCardPlugin } from '@/components/SearchBarPlugin';
|
||||
import { post } from '@/services/ajax';
|
||||
import { DownOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Button, Col, Form, Input, Pagination, Row, Select, Table } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { ChatBar, ChatTime } from './ChatBar';
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
}
|
||||
import { Form, Input, Tabs } from 'antd';
|
||||
import Spin from 'antd/lib/spin';
|
||||
import { stringify } from 'qs';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { IChat, ICustFollow, IStaffsItem } from './ChatLogsType';
|
||||
import { ChatBar } from './components/ChatBar';
|
||||
import { ChatTime } from './components/ChatTime';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const ChatLogs: React.FC = () => {
|
||||
const [param, setParam] = useState({
|
||||
const [param] = useState({
|
||||
curr_page: 1,
|
||||
page_count: 20,
|
||||
msg_from: '',
|
||||
msg_to_list: '',
|
||||
});
|
||||
const tabKeyRef = useRef('0');
|
||||
|
||||
const data: DataType[] = [
|
||||
const [selectStaff, setSelectStaff] = useState<IStaffsItem>();
|
||||
const selectStaffRef = useRef<IStaffsItem>();
|
||||
const [staffsList, setStaffsList] = useState<IStaffsItem[]>([]);
|
||||
const [custFollowsList, setCustFollowsList] = useState<ICustFollow[]>([]);
|
||||
|
||||
const [selectCustFollow, setCustFollow] = useState<ICustFollow>();
|
||||
const selectCustFollowRef = useRef<ICustFollow>();
|
||||
const [chatLogs, setChatLogs] = useState<IChat[]>([]);
|
||||
|
||||
const [tabs] = useState([
|
||||
{
|
||||
key: '0',
|
||||
label: '内部联系人',
|
||||
children: '',
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
firstName: 'John',
|
||||
lastName: 'Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer'],
|
||||
label: '外部联系人',
|
||||
children: '',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
firstName: 'Jim',
|
||||
lastName: 'Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
label: '客户群聊',
|
||||
children: '',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstName: 'Joe',
|
||||
lastName: 'Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const [flolowsBoxShow, setFlolowsBox] = useState(false);
|
||||
const timeDiffRef = useRef('');
|
||||
const timeShowRef = useRef(false);
|
||||
const chatBoxRef = useRef<any>();
|
||||
const isAllChatRef = useRef(false);
|
||||
const chatLogLoadingRef = useRef(false);
|
||||
const [chatLogLoading, setChatLogLoading] = useState(false);
|
||||
|
||||
function show() {
|
||||
setFlolowsBox(false);
|
||||
}
|
||||
|
||||
const callback = function (mutationsList: any) {
|
||||
// Use traditional 'for loops' for IE 11
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
document.querySelector('.curr_page' + param.curr_page)?.scrollIntoView(true);
|
||||
} else if (mutation.type === 'attributes') {
|
||||
console.log('The ' + mutation.attributeName + ' attribute was modified.');
|
||||
}
|
||||
}
|
||||
};
|
||||
const observer = new MutationObserver(callback);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', show, false);
|
||||
getStaffsList();
|
||||
|
||||
observer.observe(chatBoxRef.current, { childList: true });
|
||||
return () => {
|
||||
document.removeEventListener('click', show, false);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 获取员工
|
||||
const getStaffsList = () => {
|
||||
post({ url: '/Staffs/Data' }).then((res) => {
|
||||
if (res.err_code == 0) {
|
||||
if (Array.isArray(res.data)) {
|
||||
// setSelectStaff(res.data[0]);
|
||||
// selectStaffRef.current = res.data[0];
|
||||
res.data.forEach((element: IStaffsItem) => {
|
||||
if (element.user_id == 'yangxb') {
|
||||
setSelectStaff(element);
|
||||
selectStaffRef.current = element;
|
||||
}
|
||||
});
|
||||
setStaffsList(res.data);
|
||||
getCustFollowsList();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const page = (curr: number) => {
|
||||
param.curr_page = curr;
|
||||
param.msg_from = selectStaffRef.current?.user_id + '';
|
||||
param.msg_to_list = selectCustFollowRef.current?.cust_id + '';
|
||||
timeDiffRef.current = '';
|
||||
timeShowRef.current = false;
|
||||
getChatLogsList();
|
||||
};
|
||||
|
||||
const getChatLogsList = () => {
|
||||
chatLogLoadingRef.current = true;
|
||||
setChatLogLoading(true);
|
||||
post({
|
||||
url: '/ChatLogs/List',
|
||||
data: stringify(param),
|
||||
}).then((res) => {
|
||||
const count = res.count || 0;
|
||||
chatLogLoadingRef.current = false;
|
||||
setChatLogLoading(false);
|
||||
isAllChatRef.current = count < param.page_count * param.curr_page;
|
||||
if (res.err_code == 0) {
|
||||
if (Array.isArray(res.data)) {
|
||||
if (param.curr_page == 1) {
|
||||
setChatLogs([...res.data, { curr_page: param.curr_page }]);
|
||||
} else {
|
||||
setChatLogs([...res.data, { curr_page: param.curr_page }, ...chatLogs]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getCustFollowsList = () => {
|
||||
post({
|
||||
url: '/CustFollows/List',
|
||||
data: stringify({ user_id: selectStaffRef.current?.user_id }),
|
||||
}).then((res) => {
|
||||
if (res.err_code == 0) {
|
||||
if (Array.isArray(res.data)) {
|
||||
setCustFollowsList(res.data);
|
||||
if (res.data.length) {
|
||||
setCustFollow(res.data[0]);
|
||||
selectCustFollowRef.current = res.data[0];
|
||||
page(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// const { notification } = App.useApp();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<SearchBarPlugin>
|
||||
<Form>
|
||||
<Row gutter={{ xs: 0, sm: 16 }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
{ value: 'disabled', label: 'Disabled', disabled: true },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</SearchBarPlugin>
|
||||
<SearchBottonsCardPlugin>
|
||||
<Row justify={'center'}>
|
||||
<Button type="primary">搜索</Button>
|
||||
</Row>
|
||||
</SearchBottonsCardPlugin>
|
||||
<ChatTime msgtime={1680761136755}></ChatTime>
|
||||
<ChatBar></ChatBar>
|
||||
<Table
|
||||
size="middle"
|
||||
dataSource={data}
|
||||
style={{ padding: 16, borderRadius: 6, background: '#fff', marginTop: 16 }}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
>
|
||||
<Table.Column title="ID" dataIndex={'key'}></Table.Column>
|
||||
<Table.Column title="firstName" dataIndex={'firstName'}></Table.Column>
|
||||
</Table>
|
||||
<div className={styles.box}>
|
||||
<div className={styles.personnelBox}>
|
||||
<div className={`${styles.flolowsBox} ${flolowsBoxShow ? styles.show : ''}`}>
|
||||
{staffsList.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.user_id}
|
||||
className={styles.chatB}
|
||||
onClick={(e) => {
|
||||
setSelectStaff({ ...item });
|
||||
selectStaffRef.current = { ...item };
|
||||
setFlolowsBox(false);
|
||||
getCustFollowsList();
|
||||
}}
|
||||
>
|
||||
<div className={styles.avatar}>{item.name[0]}</div>
|
||||
<div className={styles.chatAMsg}>
|
||||
<div className={styles.chatAName} title={item.name}>
|
||||
{item.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.chatA}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setFlolowsBox(!flolowsBoxShow);
|
||||
}}
|
||||
>
|
||||
{selectStaff ? (
|
||||
<>
|
||||
<div className={styles.avatar}>{selectStaff.name[0]}</div>
|
||||
<div className={styles.chatAMsg}>
|
||||
<div className={styles.chatAName} title={selectStaff.name}>
|
||||
{selectStaff.name}
|
||||
</div>
|
||||
</div>
|
||||
{flolowsBoxShow ? <UpOutlined /> : <DownOutlined />}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<Form autoComplete="off">
|
||||
<Input
|
||||
style={{ margin: '0 12px', width: 'calc(100% - 24px)' }}
|
||||
autoComplete="off"
|
||||
></Input>
|
||||
</Form>
|
||||
<Tabs items={tabs} size="small" style={{ padding: '0 12px' }} tabBarGutter={12}></Tabs>
|
||||
<div className={styles.chatBBox}>
|
||||
{custFollowsList.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.cust_id}
|
||||
className={`${styles.chatB} ${
|
||||
selectCustFollow?.cust_id == item.cust_id ? styles.active : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
setCustFollow(item);
|
||||
selectCustFollowRef.current = item;
|
||||
page(1);
|
||||
}}
|
||||
>
|
||||
<div className={styles.avatar}>
|
||||
<img
|
||||
src={item.cust_info.avatar}
|
||||
alt=""
|
||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.chatAMsg}>
|
||||
<div className={styles.chatAName} title={item.cust_info.name}>
|
||||
{item.cust_info.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className={styles.logTop}>
|
||||
<div>{selectCustFollowRef.current?.cust_info.name}</div>
|
||||
</div>
|
||||
<Spin spinning={chatLogLoading} style={{ display: 'block' }}>
|
||||
<div
|
||||
className={styles.chatLogBox}
|
||||
ref={chatBoxRef}
|
||||
onScroll={(e) => {
|
||||
if (
|
||||
e.target.scrollTop == 0 &&
|
||||
!isAllChatRef.current &&
|
||||
!chatLogLoadingRef.current
|
||||
) {
|
||||
page(param.curr_page + 1);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isAllChatRef.current ? (
|
||||
<div style={{ marginBottom: 12, textAlign: 'center', color: '#999' }}>
|
||||
没有更多聊天记录了
|
||||
</div>
|
||||
) : null}
|
||||
{chatLogs.map((item, i) => {
|
||||
if (item.curr_page) {
|
||||
return (
|
||||
<div
|
||||
key={item.curr_page}
|
||||
className={`curr_page${param.curr_page}`}
|
||||
style={{ height: 0 }}
|
||||
></div>
|
||||
);
|
||||
} else {
|
||||
if (timeDiffRef.current == '') {
|
||||
timeDiffRef.current = item.msg_time;
|
||||
timeShowRef.current = false;
|
||||
} else {
|
||||
if (
|
||||
Date.parse(item.msg_time) - Date.parse(timeDiffRef.current) >
|
||||
5 * 60 * 1000
|
||||
) {
|
||||
timeDiffRef.current = item.msg_time;
|
||||
timeShowRef.current = true;
|
||||
} else {
|
||||
timeShowRef.current = false;
|
||||
}
|
||||
}
|
||||
|
||||
<Pagination
|
||||
style={{
|
||||
background: '#fff',
|
||||
borderRadius: 6,
|
||||
marginTop: 16,
|
||||
padding: 16,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
current={param.curr_page}
|
||||
pageSize={param.page_count}
|
||||
total={1000}
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
onShowSizeChange={(current, size) => {
|
||||
param.page_count = size;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
showTotal={(total, range) => {
|
||||
return <span style={{ lineHeight: 1 }}>共{total}条</span>;
|
||||
}}
|
||||
onChange={(page, pageSize) => {
|
||||
param.curr_page = page;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
/>
|
||||
if (i == 0) {
|
||||
timeShowRef.current = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={item.msg_id}>
|
||||
{timeShowRef.current ? <ChatTime msgtime={item.msg_time}></ChatTime> : null}
|
||||
<ChatBar from={selectStaff} to={selectCustFollow} chat={item}></ChatBar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
@@ -1,187 +1,93 @@
|
||||
import { SearchBarPlugin, SearchBottonsCardPlugin } from '@/components/SearchBarPlugin';
|
||||
import { post } from '@/services/ajax';
|
||||
import { DeleteOutlined, FormOutlined } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
Pagination,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Select,
|
||||
Table,
|
||||
Tree,
|
||||
} from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Col, Form, Input, Pagination, Row, Spin, Table, Tree } from 'antd';
|
||||
import { stringify } from 'qs';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
interface IDepartment {
|
||||
children: null | IDepartment[];
|
||||
department_leader: string;
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
interface IStaffsItem {
|
||||
user_id: string | number;
|
||||
name: string;
|
||||
telephone: string;
|
||||
dep_name: string[];
|
||||
position: string;
|
||||
}
|
||||
|
||||
interface IStaffsData {
|
||||
count: number;
|
||||
data?: IStaffsItem[];
|
||||
}
|
||||
|
||||
type Param = {
|
||||
curr_page: number;
|
||||
page_count: number;
|
||||
dep_id: number;
|
||||
name?: string;
|
||||
position?: string;
|
||||
telephone?: string;
|
||||
};
|
||||
|
||||
const DepartmentsList: React.FC = () => {
|
||||
const [param, setParam] = useState({
|
||||
const [param] = useState<Param>({
|
||||
curr_page: 1,
|
||||
page_count: 20,
|
||||
dep_id: 0,
|
||||
});
|
||||
|
||||
const [departmentID, setDepartmentsID] = useState(1);
|
||||
const [departmentID, setDepartmentsID] = useState<number>(0);
|
||||
const [departmentsList, setDepartmentsList] = useState<IDepartment[]>([]);
|
||||
const [staffsData, setStaffsData] = useState<IStaffsData>({ count: 0, data: [] });
|
||||
const [loadingL, setLoadingL] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const departments = [
|
||||
{
|
||||
id: 1,
|
||||
name: '福州晨丰科技有限公司',
|
||||
parent_id: 0,
|
||||
sort: 2147483447,
|
||||
department_leader: 'chenf,LiXiang,CFRS',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: '总经办',
|
||||
parent_id: 1,
|
||||
sort: 2147483447,
|
||||
department_leader: 'chenf',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '研发部',
|
||||
parent_id: 1,
|
||||
sort: 2147483247,
|
||||
department_leader: 'yangxb',
|
||||
children: [
|
||||
{
|
||||
id: 18,
|
||||
name: '.Net组',
|
||||
parent_id: 3,
|
||||
sort: 100005000,
|
||||
department_leader: 'xief',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: '平台组',
|
||||
parent_id: 3,
|
||||
sort: 100004000,
|
||||
department_leader: 'yangxb',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: 'CAD组',
|
||||
parent_id: 3,
|
||||
sort: 100003000,
|
||||
department_leader: 'chenx',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
name: '测试组',
|
||||
parent_id: 3,
|
||||
sort: 100001000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
name: '是的防守对方',
|
||||
parent_id: 3,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
name: '12',
|
||||
parent_id: 3,
|
||||
sort: 99999000,
|
||||
department_leader: '',
|
||||
children: [
|
||||
{
|
||||
id: 25,
|
||||
name: '3',
|
||||
parent_id: 24,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 181,
|
||||
name: '.Net组',
|
||||
parent_id: 3,
|
||||
sort: 100005000,
|
||||
department_leader: 'xief',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 191,
|
||||
name: '平台组',
|
||||
parent_id: 3,
|
||||
sort: 100004000,
|
||||
department_leader: 'yangxb',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 201,
|
||||
name: 'CAD组',
|
||||
parent_id: 3,
|
||||
sort: 100003000,
|
||||
department_leader: 'chenx',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 211,
|
||||
name: '测试组',
|
||||
parent_id: 3,
|
||||
sort: 100001000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 231,
|
||||
name: '是的防守对方是的防守对方是的防守对方是的防守对方',
|
||||
parent_id: 3,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const getDepartmentsList = () => {
|
||||
setLoadingL(true);
|
||||
post({ url: '/Departments/List' }).then((res) => {
|
||||
setLoadingL(false);
|
||||
if (res.err_code == 0) {
|
||||
if (Array.isArray(res.data)) {
|
||||
param.dep_id = res.data[0].id;
|
||||
setDepartmentsID(param.dep_id);
|
||||
setDepartmentsList(res.data);
|
||||
getStaffsList();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const getStaffsList = () => {
|
||||
setLoading(true);
|
||||
post({ url: '/Staffs/List', data: stringify(param) }).then((res) => {
|
||||
setLoading(false);
|
||||
if (res.err_code == 0) {
|
||||
if (!Array.isArray(res.data)) {
|
||||
res.data = [];
|
||||
}
|
||||
setStaffsData(res as IStaffsData);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const data: DataType[] = [
|
||||
{
|
||||
key: '2',
|
||||
firstName: 'Jim',
|
||||
lastName: 'Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstName: 'Joe',
|
||||
lastName: 'Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
];
|
||||
const page = (page: number) => {
|
||||
param.curr_page = page;
|
||||
getStaffsList();
|
||||
};
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [popOpen, setPopOpen] = useState(-1);
|
||||
useEffect(() => {
|
||||
getDepartmentsList();
|
||||
}, []);
|
||||
|
||||
// const [open, setOpen] = useState(false);
|
||||
// const [popOpen, setPopOpen] = useState(-1);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
@@ -200,28 +106,30 @@ const DepartmentsList: React.FC = () => {
|
||||
padding: '12px 0',
|
||||
}}
|
||||
>
|
||||
<Tree
|
||||
className={'department-tree'}
|
||||
blockNode
|
||||
selectedKeys={[departmentID]}
|
||||
defaultExpandAll
|
||||
style={{}}
|
||||
treeData={departments}
|
||||
fieldNames={{ title: 'name', key: 'id' }}
|
||||
onSelect={(selectedKeys) => {
|
||||
console.log(selectedKeys);
|
||||
if (selectedKeys.length) {
|
||||
setDepartmentsID(Number(selectedKeys[0]));
|
||||
}
|
||||
}}
|
||||
titleRender={(nodeData: any) => {
|
||||
// console.log(nodeData);
|
||||
return (
|
||||
<div className={styles.departmentItem}>
|
||||
<div className={styles.name} title={nodeData.name}>
|
||||
{nodeData.name}
|
||||
</div>
|
||||
<div className={styles.btnsBox}>
|
||||
<Spin spinning={loadingL} style={{ display: 'block' }}>
|
||||
{departmentsList.length ? (
|
||||
<Tree
|
||||
className={'department-tree'}
|
||||
blockNode
|
||||
selectedKeys={[departmentID]}
|
||||
defaultExpandAll
|
||||
treeData={departmentsList}
|
||||
fieldNames={{ title: 'name', key: 'id' }}
|
||||
onSelect={(selectedKeys) => {
|
||||
if (selectedKeys.length) {
|
||||
setDepartmentsID(Number(selectedKeys[0]));
|
||||
param.dep_id = Number(selectedKeys[0]);
|
||||
page(1);
|
||||
}
|
||||
}}
|
||||
titleRender={(nodeData: any) => {
|
||||
// console.log(nodeData);
|
||||
return (
|
||||
<div className={styles.departmentItem}>
|
||||
<div className={styles.name} title={nodeData.name}>
|
||||
{nodeData.name}
|
||||
</div>
|
||||
{/* <div className={styles.btnsBox}>
|
||||
<FormOutlined
|
||||
title="修改"
|
||||
onClick={(e) => {
|
||||
@@ -251,13 +159,15 @@ const DepartmentsList: React.FC = () => {
|
||||
className={styles.del}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
></Tree>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
></Tree>
|
||||
) : null}
|
||||
</Spin>
|
||||
</div>
|
||||
<Modal
|
||||
{/* <Modal
|
||||
open={open}
|
||||
title="修改部门"
|
||||
onCancel={() => setOpen(false)}
|
||||
@@ -269,12 +179,48 @@ const DepartmentsList: React.FC = () => {
|
||||
<Input type="text"></Input>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Modal> */}
|
||||
<div style={{ flexGrow: 1, minWidth: 0 }}>
|
||||
<SearchBarPlugin>
|
||||
<Form>
|
||||
<Form autoComplete="off">
|
||||
<Row gutter={{ xs: 0, sm: 16 }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Col xs={24} sm={12} md={8}>
|
||||
<Form.Item label="姓名">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
param.name = e.target.value.trim();
|
||||
}}
|
||||
allowClear
|
||||
onPressEnter={() => page(1)}
|
||||
></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={8}>
|
||||
<Form.Item label="职位">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
param.position = e.target.value.trim();
|
||||
}}
|
||||
allowClear
|
||||
onPressEnter={() => page(1)}
|
||||
></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={8}>
|
||||
<Form.Item label="手机号">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
param.telephone = e.target.value.trim();
|
||||
}}
|
||||
allowClear
|
||||
onPressEnter={() => page(1)}
|
||||
></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{/* <Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
@@ -287,47 +233,7 @@ const DepartmentsList: React.FC = () => {
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
{ value: 'disabled', label: 'Disabled', disabled: true },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Col> */}
|
||||
</Row>
|
||||
</Form>
|
||||
</SearchBarPlugin>
|
||||
@@ -336,9 +242,7 @@ const DepartmentsList: React.FC = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
post({
|
||||
url: '/Departments/List',
|
||||
}).then((res) => {});
|
||||
page(1);
|
||||
}}
|
||||
>
|
||||
搜索
|
||||
@@ -346,30 +250,19 @@ const DepartmentsList: React.FC = () => {
|
||||
</Row>
|
||||
</SearchBottonsCardPlugin>
|
||||
<Table
|
||||
tableLayout="fixed"
|
||||
size="middle"
|
||||
dataSource={data}
|
||||
dataSource={staffsData.data}
|
||||
style={{ padding: 16, borderRadius: 6, background: '#fff', marginTop: 16 }}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
rowKey={'user_id'}
|
||||
loading={loading}
|
||||
>
|
||||
<Table.Column title="ID" dataIndex={'key'}></Table.Column>
|
||||
<Table.Column title="firstName" dataIndex={'firstName'}></Table.Column>
|
||||
<Table.Column
|
||||
width={90}
|
||||
title="操作"
|
||||
render={(_text, record: any) => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
console.log(record);
|
||||
}}
|
||||
>
|
||||
{record.firstName}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
></Table.Column>
|
||||
<Table.Column title="姓名" dataIndex={'name'}></Table.Column>
|
||||
<Table.Column title="职位" dataIndex={'position'}></Table.Column>
|
||||
<Table.Column title="部门" dataIndex={'dep_name'}></Table.Column>
|
||||
<Table.Column title="手机号" dataIndex={'telephone'}></Table.Column>
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
@@ -384,18 +277,18 @@ const DepartmentsList: React.FC = () => {
|
||||
}}
|
||||
current={param.curr_page}
|
||||
pageSize={param.page_count}
|
||||
total={1000}
|
||||
total={staffsData.count}
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
onShowSizeChange={(current, size) => {
|
||||
param.page_count = size;
|
||||
setParam({ ...param });
|
||||
// setParam({ ...param });
|
||||
page(1);
|
||||
}}
|
||||
showTotal={(total, range) => {
|
||||
return <span style={{ lineHeight: 1 }}>共{total}条</span>;
|
||||
}}
|
||||
onChange={(page, pageSize) => {
|
||||
param.curr_page = page;
|
||||
setParam({ ...param });
|
||||
onChange={(curr, pageSize) => {
|
||||
page(curr);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { RequestOptions } from '@@/plugin-request/request';
|
||||
import type { RequestConfig } from '@umijs/max';
|
||||
import { message, notification } from 'antd';
|
||||
import { notificationError } from './services/utils';
|
||||
|
||||
// 错误处理方案: 错误类型
|
||||
enum ErrorShowType {
|
||||
@@ -51,13 +51,13 @@ export const errorConfig: RequestConfig = {
|
||||
// do nothing
|
||||
break;
|
||||
case ErrorShowType.WARN_MESSAGE:
|
||||
message.warning(errorMessage);
|
||||
notificationError({ message: errorMessage });
|
||||
break;
|
||||
case ErrorShowType.ERROR_MESSAGE:
|
||||
message.error(errorMessage);
|
||||
notificationError({ message: errorMessage });
|
||||
break;
|
||||
case ErrorShowType.NOTIFICATION:
|
||||
notification.open({
|
||||
notificationError({
|
||||
description: errorMessage,
|
||||
message: errorCode,
|
||||
});
|
||||
@@ -66,21 +66,23 @@ export const errorConfig: RequestConfig = {
|
||||
// TODO: redirect
|
||||
break;
|
||||
default:
|
||||
message.error(errorMessage);
|
||||
notificationError({ message: errorMessage });
|
||||
}
|
||||
}
|
||||
} else if (error.response) {
|
||||
// Axios 的错误
|
||||
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
||||
message.error(`Response status:${error.response.status}`);
|
||||
notificationError({
|
||||
message: `Response status:${error.response.status}`,
|
||||
});
|
||||
} else if (error.request) {
|
||||
// 请求已经成功发起,但没有收到响应
|
||||
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
|
||||
// 而在node.js中是 http.ClientRequest 的实例
|
||||
message.error('None response! Please retry.');
|
||||
notificationError({ message: 'None response! Please retry.' });
|
||||
} else {
|
||||
// 发送请求时出了点问题
|
||||
message.error('Request error, please retry.');
|
||||
notificationError({ message: 'Request error, please retry.' });
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -102,7 +104,7 @@ export const errorConfig: RequestConfig = {
|
||||
const { data } = response as unknown as ResponseStructure;
|
||||
|
||||
if (data?.success === false) {
|
||||
message.error('请求失败!');
|
||||
notificationError({ message: '请求失败!' });
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { notificationError } from './utils';
|
||||
|
||||
export interface IAjaxReturn {
|
||||
err_code: number;
|
||||
@@ -19,14 +20,10 @@ export const post = async (param: IPost) => {
|
||||
data: data,
|
||||
}).then((res: IAjaxReturn) => {
|
||||
if (res.err_code != 0 && showError) {
|
||||
if (window.NotificationCF) {
|
||||
window.NotificationCF.error({
|
||||
message: `${res.err_msg}`,
|
||||
description: `${res.err_code}:${res.err_msg}`,
|
||||
});
|
||||
} else {
|
||||
console.log('window.NotificationCF未设置');
|
||||
}
|
||||
notificationError({
|
||||
message: `${res.err_msg}`,
|
||||
description: `${res.err_code}:${res.err_msg}`,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
@@ -15,3 +15,23 @@ export const getDevice: IGetDevice = () => {
|
||||
return 'desktop';
|
||||
}
|
||||
};
|
||||
|
||||
interface IError {
|
||||
message: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只在ajax里面报错用
|
||||
* @param message IError
|
||||
*/
|
||||
export const notificationError = (message: IError) => {
|
||||
if (window.NotificationCF) {
|
||||
window.NotificationCF.error({
|
||||
message: message.message,
|
||||
description: message.description || '',
|
||||
});
|
||||
} else {
|
||||
console.log('window.NotificationCF未绑定antd的notification');
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user