这节来实现下好友/群聊页面:
现在首页是这样的:
需要在 / 下添加一个二级路由:
{
path: '/',
element: <Menu/>,
children: [
{
path: '/',
element: <Friendship/>
},
{
path: '/group',
element: <Group/>
},
{
path: 'chat',
element: <Chat/>
},
{
path: 'collection',
element: <Collection/>
},
{
path: 'notification',
element: <Notification/>
}
]
}
然后分别实现这几个组件:
src/pages/Menu/index.tsx
import { Outlet, useLocation } from "react-router-dom";
import { Menu as AntdMenu, MenuProps } from 'antd';
import './menu.css';
import { MenuClickEventHandler } from "rc-menu/lib/interface";
import { router } from "../../main";
const items: MenuProps['items'] = [
{
key: '1',
label: "好友"
},
{
key: '2',
label: "群聊"
},
{
key: '3',
label: "聊天"
},
{
key: '4',
label: "收藏"
},
{
key: '5',
label: "通知"
}
];
const handleMenuItemClick: MenuClickEventHandler = (info) => {
let path = '';
switch(info.key) {
case '1':
path = '/';
break;
case '2':
path = '/group';
break;
case '3':
path = '/chat';
break;
case '4':
path = '/collection';
break;
case '5':
path = '/notification';
break;
}
router.navigate(path);
}
export function Menu() {
const location = useLocation();
function getSelectedKeys() {
if(location.pathname === '/group') {
return ['2']
} else if(location.pathname === '/chat') {
return ['3']
} else if(location.pathname === '/collection') {
return ['4']
} else if(location.pathname === '/notification') {
return ['5']
} else {
return ['1']
}
}
return <div id="menu-container">
<div className="menu-area">
<AntdMenu
defaultSelectedKeys={getSelectedKeys()}
items={items}
onClick={handleMenuItemClick}
/>
</div>
<div className="content-area">
<Outlet></Outlet>
</div>
</div>
}
引入 antd 的 Menu 实现菜单。
渲染的时候根据 useLocation 拿到的 pathname 来设置选中的菜单项。
点击菜单项的时候用 router.push 修改路径。
这里用到的 router 需要在 index.tsx 导出:
menu.css 如下:
#menu-container {
display: flex;
flex-direction: row;
}
#menu-container .menu-area {
width: 200px;
}
然后创建 src/pages/Friendship/index.tsx
export function Friendship() {
return <div>Friendship</div>
}
src/pages/Group/index.tsx
export function Group() {
return <div>Group</div>
}
src/pages/Chat/index.tsx
export function Chat() {
return <div>Chat</div>
}
src/pages/Collection/index.tsx
export function Collection() {
return <div>Collection</div>
}
src/pages/Notification/index.tsx
export function Notification() {
return <div>Notification</div>
}
在 index.tsx 里导入这些组件后,我们跑起来看看:
npm run dev
点击菜单项的路由切换,以及刷新选中对应菜单项,都没问题。
然后来写下好友页面:
import { Badge, Button, Form, Input, Popconfirm, Table, message } from "antd";
import { useCallback, useEffect, useMemo, useState } from "react";
import './index.css';
import { ColumnsType } from "antd/es/table";
import { useForm } from "antd/es/form/Form";
interface SearchFriend {
name: string;
}
interface FriendshipSearchResult {
id: number;
username: string;
nickName: string;
headPic: string;
email: string;
}
export function Friendship() {
const [friendshipResult, setFriendshipResult] = useState<Array<FriendshipSearchResult>>([]);
const columns: ColumnsType<FriendshipSearchResult> = useMemo(() => [
{
title: '昵称',
dataIndex: 'nickName'
},
{
title: '头像',
dataIndex: 'headPic',
render: (_, record) => (
<div>
<img src={record.headPic}/>
</div>
)
},
{
title: '邮箱',
dataIndex: 'email'
},
{
title: '操作',
render: (_, record) => (
<div>
<a href="#">聊天</a>
</div>
)
}
], []);
const searchFriend = async (values: SearchFriend) => {
};
const [form ] = useForm();
useEffect(() => {
searchFriend({
name: form.getFieldValue('name')
});
}, []);
return <div id="friendship-container">
<div className="friendship-form">
<Form
form={form}
onFinish={searchFriend}
name="search"
layout='inline'
colon={false}
>
<Form.Item label="名称" name="name">
<Input />
</Form.Item>
<Form.Item label=" ">
<Button type="primary" htmlType="submit">
搜索
</Button>
</Form.Item>
</Form>
</div>
<div className="friendship-table">
<Table columns={columns} dataSource={friendshipResult} style={{width: '1000px'}}/>
</div>
</div>
}
上面是 form、下面是 table。
调用搜索接口来搜索列表数据,然后设置到 table 的 dataSource。
css 部分如下:
#friendship-container {
padding: 20px;
}
#friendship-container .friendship-form {
margin-bottom: 30px;
}
看下效果:
然后我们对接下后端接口:
当时我们的好友列表接口没支持按照昵称搜索,加一下:
return res.filter((item: User) => item.nickName.includes(name))
试下效果:
没啥问题。
然后在前端页面调用下:
在 interfaces 调用下 list 接口:
export async function friendshipList(name?: string) {
return axiosInstance.get(`/friendship/list?name=${name || ''}`);
}
组件里调用下:
const searchFriend = async (values: SearchFriend) => {
try{
const res = await friendshipList(values.name || '');
if(res.status === 201 || res.status === 200) {
setFriendshipResult(res.data.map((item: FriendshipSearchResult) => {
return {
...item,
key: item.id
}
}));
}
} catch(e: any){
message.error(e.response?.data?.message || '系统繁忙,请稍后再试');
}
};
没啥问题。
然后我们同样的方式写下 Group 组件:
之前的 /chatroom/list 接口也没支持 name 参数,加一下:
测试下:
在 interfaces 加一下:
export async function chatroomList(name: string) {
return axiosInstance.get(`/chatroom/list?name=${name}`);
}
写下 Group 组件:
import { Badge, Button, Form, Input, Popconfirm, Table, message } from "antd";
import { useCallback, useEffect, useMemo, useState } from "react";
import './index.css';
import { ColumnsType } from "antd/es/table";
import { useForm } from "antd/es/form/Form";
import { chatroomList } from "../../interfaces";
interface SearchGroup {
name: string;
}
interface GroupSearchResult {
id: number;
name: string;
createTime: Date;
}
export function Group() {
const [groupResult, setGroupResult] = useState<Array<GroupSearchResult>>([]);
const columns: ColumnsType<GroupSearchResult> = useMemo(() => [
{
title: '名称',
dataIndex: 'name'
},
{
title: '创建时间',
dataIndex: 'createTime'
},
{
title: '操作',
render: (_, record) => (
<div>
<a href="#">聊天</a>
</div>
)
}
], []);
const searchGroup = async (values: SearchGroup) => {
try{
const res = await chatroomList(values.name || '');
if(res.status === 201 || res.status === 200) {
setGroupResult(res.data.map((item: GroupSearchResult) => {
return {
...item,
key: item.id
}
}));
}
} catch(e: any){
message.error(e.response?.data?.message || '系统繁忙,请稍后再试');
}
};
const [form ] = useForm();
useEffect(() => {
searchGroup({
name: form.getFieldValue('name')
});
}, []);
return <div id="group-container">
<div className="group-form">
<Form
form={form}
onFinish={searchGroup}
name="search"
layout='inline'
colon={false}
>
<Form.Item label="名称" name="name">
<Input />
</Form.Item>
<Form.Item label=" ">
<Button type="primary" htmlType="submit">
搜索
</Button>
</Form.Item>
</Form>
</div>
<div className="group-table">
<Table columns={columns} dataSource={groupResult} style={{width: '1000px'}}/>
</div>
</div>
}
还有 css
#group-container {
padding: 20px;
}
#group-container .group-form {
margin-bottom: 30px;
}
测试下:
没啥问题。
总结
这节我们实现了好友和群聊的列表和搜索。
首先我们添加了二级路由,通过 Menu 组件实现了菜单,点击切换不同页面。
然后实现了好友列表和群聊列表,搜索框输入内容,点击搜索调用 list 接口,返回的数据设置到 table。
这样,好友和群聊列表就完成了