开发: 对客户群聊分组显示

This commit is contained in:
zhengw
2023-04-21 17:36:02 +08:00
parent e926e4cde0
commit d6c0d0a4c0
6 changed files with 293 additions and 141 deletions

View File

@@ -66,6 +66,7 @@ export interface ICustFollow {
export interface IGroup {
admin_list: string;
avatar?: string;
adminUserIDs: string[];
create_time: string;
group_id: string;
@@ -77,6 +78,7 @@ export interface IGroup {
status: number;
sync_time: string;
notice: string;
type: number;
}
export interface IGroupMembers {

View File

@@ -0,0 +1,147 @@
import { groupStatus } from '@/services/config';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useEffect, useState } from 'react';
import { IGroup } from './ChatLogsType';
import styles from './index.module.scss';
type IProps = {
groupList: IGroup[];
searchWord: string;
selectGroup: IGroup | undefined;
onClick: Function;
};
type IGroupObj = {
inner: IGroup[];
outer: IGroup[];
diss: IGroup[];
};
export const GroupListWidget: React.FC<IProps> = (props) => {
const [groupObj, setGroupObj] = useState<IGroupObj>({
inner: [],
outer: [],
diss: [],
});
const [visible, setVisible] = useState('inner');
const [overflow, setOverflow] = useState('inner');
const groupListItem = (item: IGroup) => {
return (
<div
key={item.group_id}
className={`${styles.chatB} ${item.state == 0 ? styles.state0 : ''} ${
props.selectGroupRef?.group_id == item.group_id ? styles.active : ''
}`}
onClick={() => {
props.onClick(item);
}}
>
<div className={styles.avatar}>{item.name ? item.name[0] : '群'}</div>
<div className={styles.chatAMsg} style={{ flexDirection: 'column' }}>
<div className={styles.chatAName} title={item.name}>
{item.name || '未知群名'}
</div>
<div style={{ color: '#999' }}>{groupStatus[item.status]}</div>
</div>
</div>
);
};
useEffect(() => {
let groupObjT: IGroupObj = { inner: [], outer: [], diss: [] };
props.groupList.forEach((item) => {
item.name = item.name || '未知群聊';
if (item.state == 0 && item.name.includes(props.searchWord)) {
groupObjT.diss.push(item);
} else if (item.state == 1 && item.type == 1 && item.name.includes(props.searchWord)) {
groupObjT.inner.push(item);
} else if (item.state == 1 && item.type == 2 && item.name.includes(props.searchWord)) {
groupObjT.outer.push(item);
}
});
setGroupObj(groupObjT);
}, [props.groupList, props.searchWord]);
return (
<div className={styles.groupMenu}>
<div
className={styles.groupNav}
onClick={() => {
setVisible('inner');
setOverflow('');
setTimeout(() => {
setOverflow('inner');
}, 300);
}}
>
<span></span>
{visible == 'inner' ? <UpOutlined /> : <DownOutlined />}
</div>
<div
className={`${styles.groupListContent} ${
visible == 'inner' ? styles.groupListContentShow : ''
} ${overflow == 'inner' ? styles.groupListContentShowOver : ''}`}
>
{groupObj.inner.map((item) => {
return groupListItem(item);
})}
<div style={{ lineHeight: '34px', color: '#999', textAlign: 'center' }}>
{groupObj.inner.length == 0 ? '暂无内部群聊' : ''}
</div>
</div>
<div
className={styles.groupNav}
onClick={() => {
setVisible('outer');
setOverflow('');
setTimeout(() => {
setOverflow('outer');
}, 300);
}}
>
<span></span>
{visible == 'outer' ? <UpOutlined /> : <DownOutlined />}
</div>
<div
className={`${styles.groupListContent} ${
visible == 'outer' ? styles.groupListContentShow : ''
} ${overflow == 'outer' ? styles.groupListContentShowOver : ''}`}
>
{groupObj.outer.map((item) => {
return groupListItem(item);
})}
<div style={{ lineHeight: '34px', color: '#999', textAlign: 'center' }}>
{groupObj.outer.length == 0 ? '暂无外部群聊' : ''}
</div>
</div>
<div
className={styles.groupNav}
onClick={() => {
setVisible('diss');
setOverflow('');
setTimeout(() => {
setOverflow('diss');
}, 300);
}}
>
<span></span>
{visible == 'diss' ? <UpOutlined /> : <DownOutlined />}
</div>
<div
className={`${styles.groupListContent} ${
visible == 'diss' ? styles.groupListContentShow : ''
} ${overflow == 'diss' ? styles.groupListContentShowOver : ''}`}
>
{groupObj.diss.map((item) => {
return groupListItem(item);
})}
<div style={{ lineHeight: '34px', color: '#999', textAlign: 'center' }}>
{groupObj.diss.length == 0 ? '暂无已解散群聊' : ''}
</div>
</div>
</div>
);
};

View File

@@ -31,7 +31,9 @@ export const ChatVote: React.FC<IChatItem> = (props) => {
{Array.isArray(msg?.voteitem)
? [0, 1, 2].map((item) => {
return msg?.voteitem[item] ? (
<div className={styles.voteitem}>{msg?.voteitem[item]}</div>
<div key={item} className={styles.voteitem}>
{msg?.voteitem[item]}
</div>
) : (
<></>
);

View File

@@ -22,8 +22,8 @@
border-radius: 4px;
img {
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
object-fit: cover;
background-color: #fff;
border-radius: 4px;

View File

@@ -22,9 +22,7 @@
padding: 0 12px;
border-bottom: 1px solid #ddd;
cursor: pointer;
&:first-child {
margin-top: 1px;
}
&:hover {
background-color: #bae0ff;
}
@@ -66,6 +64,7 @@
justify-content: center;
width: 40px;
height: 40px;
overflow: hidden;
line-height: 1;
background-color: #69b1ff;
border-radius: 4px;
@@ -77,13 +76,32 @@
overflow: auto;
}
.groupMenu {
position: relative;
height: calc(100vh - 212px - 164px);
.groupListContent {
height: 0;
overflow: hidden;
transition: height 0.3s;
}
.groupListContentShow {
height: calc(100% - 34px * 3);
}
.groupListContentShowOver {
overflow: auto;
}
}
.delFollowList {
position: absolute;
top: calc(100% - 34px);
z-index: 1;
width: 100%;
height: 34px;
background-color: #fff;
background-color: #f5f5f5;
transition: top 0.3s, height 0.3s;
.chatB {
margin-top: 0;
@@ -94,6 +112,7 @@
}
}
.groupNav,
.delFollowListBar {
z-index: 1;
display: flex;
@@ -102,6 +121,7 @@
height: 34px;
padding: 0 12px;
color: #1890ff;
background: #fff;
cursor: pointer;
&:hover {

View File

@@ -1,5 +1,4 @@
import { post } from '@/services/ajax';
import { groupStatus } from '@/services/config';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { Drawer, Form, Input, Tabs } from 'antd';
@@ -18,6 +17,7 @@ import {
} 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 = () => {
@@ -41,16 +41,19 @@ const ChatLogs: React.FC = () => {
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[]>([]);
@@ -84,7 +87,6 @@ const ChatLogs: React.FC = () => {
const [flolowsBoxShow, setFlolowsBox] = useState(false);
const [delFollowListShow, setDelFollowListShow] = useState(false);
const [delGroupListShow, setDelGroupListShow] = useState(false);
const timeShowRef = useRef(false);
const chatBoxRef = useRef<any>();
@@ -161,10 +163,12 @@ const ChatLogs: React.FC = () => {
};
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);
@@ -210,10 +214,12 @@ const ChatLogs: React.FC = () => {
};
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);
@@ -229,7 +235,9 @@ const ChatLogs: React.FC = () => {
// 获取员工
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]);
@@ -275,7 +283,7 @@ const ChatLogs: React.FC = () => {
}, []);
// 外部联系人Item
const custFollowsListItem = (item: ICustFollow) => {
const custFollowsListItem = (item: ICustFollow, i: number) => {
return (
<div
key={item.cust_id}
@@ -294,7 +302,10 @@ const ChatLogs: React.FC = () => {
selectCustFollowRef.current = item;
page(1);
}}
style={{ display: item.name.includes(searchWord['1']) ? '' : 'none' }}
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
@@ -313,37 +324,22 @@ const ChatLogs: React.FC = () => {
);
};
// 客户群聊列表项
const groupListItem = (item: IGroup) => {
return (
<div
key={item.group_id}
className={`${styles.chatB} ${item.state == 0 ? styles.state0 : ''} ${
selectGroup?.group_id == item.group_id ? styles.active : ''
}`}
onClick={() => {
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);
const checkCustFollowsIsEmpty = (state: number) => {
for (const el of custFollowsList) {
if (el.name.includes(searchWord['1']) && el.state == state) {
return 1;
}
}
return 0;
};
getGroupMembersList();
}}
style={{ display: item.name.includes(searchWord['2']) ? '' : 'none' }}
>
<div className={styles.avatar}>{item.name ? item.name[0] : '群'}</div>
<div className={styles.chatAMsg} style={{ flexDirection: 'column' }}>
<div className={styles.chatAName} title={item.name}>
{item.name || '未知群名'}
</div>
<div style={{ color: '#999' }}>{groupStatus[item.status]}</div>
</div>
</div>
);
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();
@@ -351,90 +347,95 @@ const ChatLogs: React.FC = () => {
if (tabKey == '0') {
// 内部联系人
return (
<>
{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;
<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;
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={{
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}
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>
</div>
);
})
) : (
<div style={{ lineHeight: '44px', textAlign: 'center', color: '#999' }}>
</div>
)}
</>
);
})
: null}
<div style={{ lineHeight: '34px', textAlign: 'center', color: '#999' }}>
{checkInnerIsEmpty() == 0 ? '暂无内部联系人' : ''}
</div>
</Spin>
);
} else if (tabKey == '1') {
return (
<>
{custFollowsList.length ? (
custFollowsList.map((item) => {
return item.state == 0 ? null : custFollowsListItem(item);
})
) : (
<div style={{ lineHeight: '44px', textAlign: 'center', color: '#999' }}>
</div>
)}
</>
<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 (
<>
{groupList.length ? (
groupList.map((item) => {
return item.state == 0 ? null : groupListItem(item);
})
) : (
<div style={{ lineHeight: '44px', textAlign: 'center', color: '#999' }}>
</div>
)}
</>
<Spin spinning={loadingGroup}>
<GroupListWidget
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>
);
}
};
@@ -462,37 +463,17 @@ const ChatLogs: React.FC = () => {
delFollowListShow ? styles.delFollowListBoxShow : ''
}`}
>
{custFollowsList.map((item) => {
return item.state == 1 ? null : custFollowsListItem(item);
{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 (
<div
className={`${styles.delFollowList} ${delGroupListShow ? styles.delFollowListShow : ''}`}
>
<div
className={styles.delFollowListBar}
onClick={() => {
setDelGroupListShow(!delGroupListShow);
}}
>
<span></span>
{delGroupListShow ? <UpOutlined /> : <DownOutlined />}
</div>
<div
className={`${styles.delFollowListBox} ${
delGroupListShow ? styles.delFollowListBoxShow : ''
}`}
>
{groupList.map((item) => {
return item.state == 1 ? null : groupListItem(item);
})}
</div>
</div>
);
return <></>;
}
};