Files
scrm.antd/src/pages/ChatLogs/index.tsx
2023-04-24 15:12:52 +08:00

726 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { post } from '@/services/ajax';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { Drawer, Input, Tabs } from 'antd';
import Spin from 'antd/lib/spin';
import { stringify } from 'qs';
import React, { useEffect, useRef, useState } from 'react';
import { CustDetailContent } from '../CustomList/components/CustDetailContent';
import { DepartmentMembersDetail } from '../DepartmentsList/components/DepartmentMemberDetail';
import { GroupDetailContent } from '../GroupList/components/GroupDetailContent';
import { IChat, ICustFollow, IGroup, IGroupMembers, IStaffsItem } from './ChatLogsType';
import { getAdminList } from './ChatUtils';
import { ChatBar } from './components/ChatBar';
import { ChatTime } from './components/ChatTime';
import { GroupListWidget } from './GroupList';
import styles from './index.module.scss';
const ChatLogs: React.FC = () => {
const [param] = useState({
curr_page: 1,
page_count: 20,
msg_from: '',
msg_to_list: '',
room_id: '',
});
const [tabKey, setTabKey] = useState<string>('0');
const tabKeyRef = useRef('0');
const [open, setOpen] = useState(false);
// 员工
const [staffsList, setStaffsList] = useState<IStaffsItem[]>([]);
const [selectStaff, setSelectStaff] = useState<IStaffsItem>();
const selectStaffRef = useRef<IStaffsItem>();
// 内部联系人
const [innerStaffsList, setInnerStaffsList] = useState<IStaffsItem[]>([]);
const selectInnerStaffRef = useRef<IStaffsItem>();
const [selectInnerStaff, setSelectInnerStaff] = useState<IStaffsItem>();
const [loadingInner, setLoadingInner] = useState(false);
// 外部联系人
const [custFollowsList, setCustFollowsList] = useState<ICustFollow[]>([]);
const [selectCustFollow, setSelectCustFollow] = useState<ICustFollow>();
const selectCustFollowRef = useRef<ICustFollow>();
const [loadingOuter, setLoadingOuter] = useState(false);
// 群聊
const [groupList, setGroupList] = useState<IGroup[]>([]);
const selectGroupRef = useRef<IGroup>();
const [selectGroup, setSelectGroup] = useState<IGroup>();
const [loadingGroup, setLoadingGroup] = useState(false);
// 群成员
const [groupMembersList, setGroupMembersList] = useState<IGroupMembers[]>([]);
const groupMembersObjRef = useRef<any>({});
const [chatLogs, setChatLogs] = useState<IChat[]>([]);
const [tabs] = useState([
{
key: '0',
label: '内部联系人',
children: '',
},
{
key: '1',
label: '外部联系人',
children: '',
},
{
key: '2',
label: '客户群聊',
children: '',
},
]);
const [searchWord, setSearchWord] = useState<any>({
'0': '',
'1': '',
'2': '',
});
const [followBoxShow, setFlolowBoxShow] = useState(false);
const [delFollowListShow, setDelFollowListShow] = useState(false);
const timeShowRef = useRef(false);
const chatBoxRef = useRef<any>();
const isAllChatRef = useRef(false);
const chatLogLoadingRef = useRef(false);
const [chatLogLoading, setChatLogLoading] = useState(false);
const [staffSearchWord, setStaffSearchWord] = useState('');
const avatarObjRef = useRef<any>({});
function show() {
setFlolowBoxShow(false);
}
const getChatLogsList = () => {
chatLogLoadingRef.current = true;
setChatLogLoading(true);
post({
url: tabKeyRef.current == '2' ? '/ChatLogs/GroupList' : '/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) {
let arr: IChat[] = [];
if (Array.isArray(res.data) && res.data.length) {
const temp = res.data.reverse();
const mark = { curr_page: param.curr_page, msg_time: temp[temp.length - 1].msg_time };
if (param.curr_page == 1) {
arr = [...temp, mark];
} else {
arr = [...temp, mark, ...chatLogs];
}
// 5分钟外显示时间
// if (arr) {
// let show_time = '';
// arr.forEach((el, i) => {
// el.show_time = false;
// if (i == 0) {
// el.show_time = true;
// } else {
// if (show_time == '') {
// show_time = el.msg_time;
// } else {
// if (
// new Date(el.msg_time).getTime() - new Date(show_time).getTime() >
// 5 * 60 * 1000
// ) {
// el.show_time = true;
// show_time = el.msg_time;
// }
// }
// }
// });
// }
}
setChatLogs(arr);
}
});
};
const page = (curr: number) => {
param.curr_page = curr;
param.msg_from = selectStaffRef.current?.user_id + '';
if (tabKeyRef.current == '0') {
param.msg_to_list = selectInnerStaffRef.current?.user_id + '';
} else if (tabKeyRef.current == '1') {
param.msg_to_list = selectCustFollowRef.current?.cust_id + '';
} else {
param.room_id = selectGroupRef.current?.group_id + '';
}
timeShowRef.current = false;
getChatLogsList();
};
const getGroupList = () => {
setLoadingGroup(true);
post({
url: '/Groups/ChatGroups',
data: stringify({ user_id: selectStaffRef.current?.user_id }),
}).then((res) => {
setLoadingGroup(false);
if (res.err_code == 0) {
if (Array.isArray(res.data)) {
setGroupList(res.data);
// if (res.data.length) {
// setCustFollow(res.data[0]);
// selectCustFollowRef.current = res.data[0];
// page(1);
// }
}
avatarObjRef.current = res.avatar || {};
}
});
};
// 群成员
const getGroupMembersList = () => {
post({
url: '/GroupMembers/GroupMembersList',
data: stringify({ group_id: selectGroupRef.current?.group_id }),
}).then((res) => {
if (res.err_code == 0) {
if (Array.isArray(res.data)) {
groupMembersObjRef.current = {};
let owner: IGroupMembers[] = [];
let admin_list: IGroupMembers[] = [];
let other: IGroupMembers[] = [];
res.data.forEach((item: IGroupMembers) => {
item.avatar = item.staff_avatar || item.avatar;
groupMembersObjRef.current[item.user_id] = item;
if (item.user_id == selectGroupRef.current?.owner) {
owner.push(item);
} else if (selectGroupRef.current?.adminUserIDs.includes(item.user_id)) {
admin_list.push(item);
} else {
other.push(item);
}
});
// 对群员 群主 > 管理员 > 普通成员 排序
setGroupMembersList([...owner, ...admin_list, ...other]);
page(1);
}
}
});
};
const getCustFollowsList = () => {
setLoadingOuter(true);
post({
url: '/CustFollows/List',
data: stringify({ user_id: selectStaffRef.current?.user_id }),
}).then((res) => {
setLoadingOuter(false);
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 getStaffsList = () => {
setLoadingInner(true);
post({ url: '/Staffs/Data' }).then((res) => {
setLoadingInner(false);
if (res.err_code == 0) {
if (Array.isArray(res.data) && res.data.length) {
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);
setInnerStaffsList(res.data);
getCustFollowsList();
getGroupList();
}
}
});
};
// 监听DOM变动
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();
};
}, []);
// 外部联系人Item
const custFollowsListItem = (item: ICustFollow, i: number) => {
return (
<div
key={item.cust_id}
className={`${styles.chatB} ${item.state == 0 ? styles.state0 : ''} ${
selectCustFollow?.cust_id == item.cust_id ? styles.active : ''
}`}
onClick={() => {
tabKeyRef.current = tabKey;
setSelectInnerStaff(undefined);
selectInnerStaffRef.current = undefined;
setSelectGroup(undefined);
selectGroupRef.current = undefined;
setSelectCustFollow(item);
selectCustFollowRef.current = item;
page(1);
}}
style={{
display: item.name.includes(searchWord['1']) ? '' : 'none',
marginTop: i == 0 && item.state == 1 ? 1 : 0,
}}
>
<div className={styles.avatar} style={{ background: item?.avatar ? '#fff' : '' }}>
<img
src={item.avatar}
alt=""
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'cover' }}
/>
</div>
<div className={styles.chatAMsg} style={{ flexDirection: 'column' }}>
<div className={styles.chatAName} title={item.name}>
{item.name}
</div>
<div>{item.remark}</div>
</div>
</div>
);
};
const checkCustFollowsIsEmpty = (state: number) => {
let count = 0;
for (const el of custFollowsList) {
if (el.name.includes(searchWord['1']) && el.state == state) {
count += 1;
}
}
return count;
};
const checkInnerIsEmpty = () => {
for (const el of innerStaffsList) {
if (el.name.includes(searchWord['0']) && selectInnerStaff?.user_id != el.user_id) {
return 1;
}
}
return 0;
};
// const { notification } = App.useApp();
const tabContent = () => {
if (tabKey == '0') {
// 内部联系人
return (
<Spin spinning={loadingInner}>
{innerStaffsList.length
? innerStaffsList.map((item) => {
if (item.user_id == selectStaff?.user_id) {
return null;
}
return (
<div
key={`${item.user_id}_${item.name}`}
className={`${styles.chatB} ${
selectInnerStaff?.user_id == item.user_id ? styles.active : ''
}`}
onClick={() => {
tabKeyRef.current = tabKey;
setSelectCustFollow(undefined);
selectCustFollowRef.current = undefined;
setSelectGroup(undefined);
selectGroupRef.current = undefined;
setSelectInnerStaff(item);
selectInnerStaffRef.current = item;
page(1);
}}
style={{ display: item.name.includes(searchWord['0']) ? '' : 'none' }}
>
<div className={styles.avatar}>
{item.avatar ? (
<img
src={item.avatar}
alt=""
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
borderRadius: 4,
}}
/>
) : (
<>{item.name[0]}</>
)}
</div>
<div className={styles.chatAMsg}>
<div className={styles.chatAName} title={item.name}>
{item.name}
</div>
</div>
</div>
);
})
: null}
<div style={{ lineHeight: '34px', textAlign: 'center', color: '#999' }}>
{checkInnerIsEmpty() == 0 ? '暂无内部联系人' : ''}
</div>
</Spin>
);
} else if (tabKey == '1') {
return (
<Spin spinning={loadingOuter}>
{custFollowsList.length
? custFollowsList.map((item, i) => {
return item.state == 0 ? null : custFollowsListItem(item, i);
})
: null}
<div style={{ lineHeight: '34px', textAlign: 'center', color: '#999' }}>
{checkCustFollowsIsEmpty(1) == 0 ? '暂无外部联系人' : ''}
</div>
</Spin>
);
} else {
return (
<Spin spinning={loadingGroup}>
<GroupListWidget
groupAvatar={avatarObjRef.current}
groupList={groupList}
searchWord={searchWord['2']}
onClick={(item: IGroup) => {
tabKeyRef.current = tabKey;
setSelectCustFollow(undefined);
selectCustFollowRef.current = undefined;
setSelectInnerStaff(undefined);
selectInnerStaffRef.current = undefined;
setSelectGroup(item);
selectGroupRef.current = item;
selectGroupRef.current.adminUserIDs = getAdminList(item.admin_list);
getGroupMembersList();
}}
selectGroup={selectGroup}
/>
</Spin>
);
}
};
// 删除的联系人/群
const tabContentDel = () => {
if (tabKey == '0') {
return <></>;
} else if (tabKey == '1') {
return (
<div
className={`${styles.delFollowList} ${delFollowListShow ? styles.delFollowListShow : ''}`}
>
<div
className={styles.delFollowListBar}
onClick={() => {
setDelFollowListShow(!delFollowListShow);
}}
>
<span>{checkCustFollowsIsEmpty(0)}</span>
{delFollowListShow ? <UpOutlined /> : <DownOutlined />}
</div>
<div
className={`${styles.delFollowListBox} ${
delFollowListShow ? styles.delFollowListBoxShow : ''
}`}
>
{custFollowsList.map((item, i) => {
return item.state == 1 ? null : custFollowsListItem(item, i);
})}
<div style={{ lineHeight: '34px', color: '#999', textAlign: 'center' }}>
{checkCustFollowsIsEmpty(0) == 0 ? '暂无已删联系人' : ''}
</div>
</div>
</div>
);
} else {
return <></>;
}
};
return (
<PageContainer>
<div className={styles.box}>
<div className={styles.personnelBox}>
<div className={`${styles.flolowsBox} ${followBoxShow ? styles.show : ''}`}>
<div
onClick={(e) => {
e.stopPropagation();
}}
style={{ padding: '12px 0', background: '#fff', position: 'sticky', top: 0 }}
>
<Input
placeholder="搜索"
style={{ margin: '0 12px', width: 'calc(100% - 24px)' }}
defaultValue={staffSearchWord}
onChange={(e) => {
setStaffSearchWord(e.target.value.trim());
}}
allowClear
/>
</div>
{staffsList.map((item) => {
if (!item.name.includes(staffSearchWord)) return null;
return (
<div
key={item.user_id}
className={styles.chatB}
onClick={() => {
setSelectCustFollow(undefined);
selectCustFollowRef.current = undefined;
setSelectInnerStaff(undefined);
selectInnerStaffRef.current = undefined;
setSelectGroup(undefined);
selectGroupRef.current = undefined;
isAllChatRef.current = false;
setChatLogs([]);
setSelectStaff({ ...item });
selectStaffRef.current = { ...item };
setFlolowBoxShow(false);
// if (tabKey == '1') {
getCustFollowsList();
// } else if (tabKey == '2') {
getGroupList();
// }
}}
style={{ background: selectStaff?.user_id == item.user_id ? '#bae0ff' : '' }}
>
<div className={styles.avatar}>
{item.avatar ? (
<img
src={item.avatar}
alt=""
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'cover',
borderRadius: 4,
}}
/>
) : (
<>{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();
setFlolowBoxShow(!followBoxShow);
}}
>
{selectStaff ? (
<>
<div className={styles.avatar}>
{selectStaff.avatar ? (
<img
src={selectStaff.avatar}
alt=""
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'cover',
borderRadius: 4,
}}
/>
) : (
<>{selectStaff.name[0]}</>
)}
</div>
<div className={styles.chatAMsg}>
<div className={styles.chatAName} title={selectStaff.name}>
{selectStaff.name}
</div>
</div>
{followBoxShow ? <UpOutlined /> : <DownOutlined />}
</>
) : null}
</div>
<Input
placeholder="搜索"
style={{ margin: '0 12px', width: 'calc(100% - 24px)' }}
defaultValue={searchWord[tabKey]}
key={tabKey}
onChange={(e) => {
searchWord[tabKey] = e.target.value.trim();
setSearchWord({ ...searchWord });
}}
allowClear
/>
<Tabs
items={tabs}
size="small"
style={{ padding: '0 12px' }}
tabBarGutter={12}
onChange={(val) => {
setTabKey(val);
// if (val == '1') {
// getCustFollowsList();
// } else if (val == '2') {
// getGroupList();
// }
}}
/>
<div className={styles.chatBBox}>{tabContent()}</div>
{tabContentDel()}
</div>
<div style={{ flex: 1 }}>
<div className={styles.logTop}>
<div
style={{ cursor: 'pointer', fontSize: 18 }}
onClick={() => {
setOpen(true);
}}
>
{tabKeyRef.current == '0' ? (
<>{selectInnerStaffRef.current?.name}</>
) : tabKeyRef.current == '1' ? (
<>{selectCustFollowRef.current?.name}</>
) : (
<>
{selectGroupRef.current ? selectGroupRef.current?.name || '未知群名' : ''}
{selectGroupRef.current ? <>{groupMembersList.length}</> : null}
</>
)}
</div>
<Drawer
title={
tabKeyRef.current == '0' ? (
<>{selectInnerStaffRef.current?.name} </>
) : tabKeyRef.current == '1' ? (
<>{selectCustFollowRef.current?.name} </>
) : (
<>
{selectGroupRef.current?.name || '未知群名'} {groupMembersList.length}
</>
)
}
open={open}
width={800}
onClose={() => setOpen(false)}
footer={false}
destroyOnClose
>
{tabKeyRef.current == '0' ? (
<DepartmentMembersDetail record={selectInnerStaff as IStaffsItem} />
) : tabKeyRef.current == '1' ? (
<CustDetailContent record={selectCustFollow as ICustFollow} />
) : (
<GroupDetailContent record={selectGroup as IGroup} />
)}
</Drawer>
</div>
<Spin spinning={chatLogLoading} style={{ display: 'block' }} size="large">
<div
className={styles.chatLogBox}
ref={chatBoxRef}
onScroll={(e: any) => {
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) => {
if (item.curr_page) {
return (
<div
key={item.curr_page}
className={`curr_page${param.curr_page}`}
style={{ height: 0 }}
/>
);
} else {
return (
<div key={item.seq}>
{/* {item.show_time ? <ChatTime msgtime={item.msg_time}></ChatTime> : null} */}
<ChatTime msgtime={item.msg_time} />
{tabKeyRef.current == '2' ? (
<ChatBar
from={selectStaff}
to={groupMembersObjRef.current[item.msg_from]}
chat={item}
/>
) : (
<ChatBar
from={selectStaff}
to={
tabKeyRef.current == '0'
? selectInnerStaffRef.current
: selectCustFollowRef.current
}
chat={item}
/>
)}
</div>
);
}
})}
</div>
</Spin>
</div>
</div>
</PageContainer>
);
};
export default ChatLogs;