Skip to content

Commit 37aed7c

Browse files
thonatosJASONPANGGO
authored andcommitted
feat: layout (#13)
* feat: update basic layout * fix: reset form after submit * feat: add message page * feat: refactor topic list
1 parent 9f257d9 commit 37aed7c

File tree

21 files changed

+494
-100
lines changed

21 files changed

+494
-100
lines changed

config/routes.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ const routes: IRoute[] = [
1919
name: '主页',
2020
component: '@/page/topic',
2121
},
22+
{
23+
path: '/my/messages',
24+
exact: true,
25+
icon: 'message',
26+
name: '未读消息',
27+
access: 'canReadMessage',
28+
component: '@/page/message',
29+
},
2230
{
2331
path: '/about',
2432
exact: true,
2533
icon: 'info',
26-
name: '关于',
34+
name: '关于我们',
2735
component: '@/page/about',
2836
},
2937
{

src/access.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export default function (initialState: InitialState) {
44
return {
55
canPostTopic: !!token,
66
canPostComment: !!token,
7+
canReadMessage: !!token,
78
};
89
}

src/component/Brand/index.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import React from 'react';
1+
import React, { useContext } from 'react';
2+
import { RouteContext } from '@ant-design/pro-layout';
3+
24
import * as styles from './index.less';
35

4-
const Brand: React.FC<Props> = ({ logo, title, description }) => (
5-
<div className={styles.container}>
6-
<img className={styles.logo} src={logo} alt="logo" />
7-
{/* <h1 className={styles.title}>{title}</h1> */}
8-
<p className={styles.description}>{description}</p>
9-
</div>
10-
);
6+
const Brand: React.FC<Props> = ({ logo, title, description }) => {
7+
const { collapsed, isMobile } = useContext(RouteContext);
8+
9+
return (
10+
<div className={styles.container}>
11+
<img className={styles.logo} src={logo} alt="logo" />
12+
{collapsed || isMobile ? null : (
13+
<p className={styles.description}>{description}</p>
14+
)}
15+
</div>
16+
);
17+
};
1118

1219
export default Brand;
1320

1421
interface Props {
15-
title: string;
16-
description: string;
1722
logo?: string;
23+
title?: string;
24+
description: string;
1825
}
File renamed without changes.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import dayjs from 'dayjs';
3+
import { useHistory } from 'umi';
4+
import { Space, Avatar, Tag } from 'antd';
5+
import { ListToolBarProps } from '@ant-design/pro-table';
6+
import ProList, { ProListMetas } from '@ant-design/pro-list';
7+
8+
import { MESSAGE_TYPE_MAP, MessageType } from '@/constants';
9+
10+
import * as styles from './index.less';
11+
12+
const MessageList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
13+
const history = useHistory();
14+
15+
const metas: ProListMetas = {
16+
avatar: {
17+
dataIndex: 'author.avatar_url',
18+
render: (_, entity: MessageModel) => {
19+
const { type: _type, author } = entity;
20+
const type = MESSAGE_TYPE_MAP[_type as MessageType];
21+
22+
return (
23+
<Space size={16}>
24+
<div
25+
style={{
26+
width: '200px',
27+
}}
28+
>
29+
<Space size={8}>
30+
<Avatar size="small" src={author.avatar_url} />
31+
<span>{author.loginname}</span>
32+
</Space>
33+
</div>
34+
35+
<Tag color={type.color}>{type.name}</Tag>
36+
</Space>
37+
);
38+
},
39+
},
40+
title: {
41+
dataIndex: 'title',
42+
valueType: 'text',
43+
render: (_, entity: MessageModel) => {
44+
return entity.topic.title;
45+
},
46+
},
47+
actions: {
48+
render: (_, entity: MessageModel) => {
49+
return dayjs(entity.create_at).fromNow();
50+
},
51+
},
52+
};
53+
54+
return (
55+
<ProList
56+
rowKey="id"
57+
showActions="always"
58+
dataSource={dataSource}
59+
loading={loading}
60+
metas={metas}
61+
className={styles.list}
62+
toolbar={toolbar}
63+
onRow={(record: MessageModel) => {
64+
return {
65+
onClick: () => {
66+
history.push(`/topic/${record.topic.id}`);
67+
},
68+
};
69+
}}
70+
/>
71+
);
72+
};
73+
74+
export default MessageList;
75+
76+
interface Props {
77+
dataSource?: MessageModel[];
78+
loading?: boolean;
79+
toolbar?: ListToolBarProps;
80+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from 'react';
2+
import { history, useModel, Link } from 'umi';
3+
import { Avatar, Space, Button, Badge, Menu, Dropdown } from 'antd';
4+
5+
const RightContent: React.FC<Props> = (props) => {
6+
const { user, logout } = useModel('user');
7+
const { count } = useModel('message');
8+
9+
if (!user) {
10+
return (
11+
<div className="cnode-header-right">
12+
<Button
13+
type="link"
14+
onClick={() => {
15+
history.push('/auth');
16+
}}
17+
>
18+
登录
19+
</Button>
20+
</div>
21+
);
22+
}
23+
24+
const { loginname, avatar_url } = user;
25+
26+
const menu = (
27+
<Menu>
28+
<Menu.Item key="profile">
29+
<Link to={`/user/${loginname}`}>个人资料</Link>
30+
</Menu.Item>
31+
<Menu.Item key="message">
32+
<Badge count={count} size="small">
33+
<Link to="/my/messages">未读消息</Link>
34+
</Badge>
35+
</Menu.Item>
36+
<Menu.Divider />
37+
<Menu.Item key="logout">
38+
<Link
39+
to="/"
40+
onClick={(e) => {
41+
e.preventDefault();
42+
logout();
43+
}}
44+
>
45+
退出登录
46+
</Link>
47+
</Menu.Item>
48+
</Menu>
49+
);
50+
51+
return (
52+
<div className="cnode-header-right">
53+
<Dropdown overlay={menu}>
54+
<Badge count={count} size="small">
55+
<Space size={8}>
56+
<Avatar shape="square" size="small" src={avatar_url} />
57+
<span>{loginname}</span>
58+
</Space>
59+
</Badge>
60+
</Dropdown>
61+
</div>
62+
);
63+
};
64+
65+
export default RightContent;
66+
67+
interface Props {}

src/component/TopicList/index.less

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.list {
2+
:global {
3+
.ant-card {
4+
padding: 0;
5+
> .ant-card-body {
6+
padding: 0;
7+
}
8+
}
9+
}
10+
}

src/component/TopicItemList/index.tsx renamed to src/component/TopicList/index.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import { TABS_MAP, TabType } from '@/constants';
2-
import ProList, { ProListMetas } from '@ant-design/pro-list';
3-
import { Space, Avatar, Tag } from 'antd';
41
import React from 'react';
52
import dayjs from 'dayjs';
63
import { useHistory } from 'umi';
4+
import { Space, Avatar, Tag } from 'antd';
75
import { ListToolBarProps } from '@ant-design/pro-table';
6+
import ProList, { ProListMetas } from '@ant-design/pro-list';
7+
8+
import { TABS_MAP, TabType } from '@/constants';
9+
810
import * as styles from './index.less';
911

10-
const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
12+
const TopicList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
1113
const history = useHistory();
1214

1315
const metas: ProListMetas = {
1416
avatar: {
1517
dataIndex: 'author.avatar_url',
16-
render: (_, entity) => {
18+
render: (_, entity: TopicModel) => {
1719
const { tab: _tab, author, reply_count, visit_count, top } = entity;
1820

1921
const category = TABS_MAP[_tab as TabType];
@@ -55,7 +57,7 @@ const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
5557
valueType: 'text',
5658
},
5759
actions: {
58-
render: (_, entity) => {
60+
render: (_, entity: TopicModel) => {
5961
const { last_reply_at } = entity;
6062
return dayjs(last_reply_at).fromNow();
6163
},
@@ -82,10 +84,10 @@ const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
8284
);
8385
};
8486

85-
export default TopicItemList;
87+
export default TopicList;
8688

8789
interface Props {
88-
dataSource?: any[];
90+
dataSource?: TopicModel[];
8991
loading?: boolean;
9092
toolbar?: ListToolBarProps;
9193
}

src/constants/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,22 @@ export const TABS_MAP = {
2121
name: '客户端测试',
2222
color: 'green',
2323
},
24-
dev: {
25-
name: '客户端测试',
24+
};
25+
26+
export type TabType = keyof typeof TABS_MAP;
27+
28+
export const MESSAGE_TYPE_MAP = {
29+
at: {
30+
name: '提到了你',
31+
color: '#108ee9',
32+
},
33+
reply: {
34+
name: '回复了你',
2635
color: 'green',
2736
},
2837
};
2938

30-
export type TabType = keyof typeof TABS_MAP;
39+
export type MessageType = keyof typeof MESSAGE_TYPE_MAP;
3140

3241
export enum FORM_TYPE {
3342
LOGIN = 'login',

src/layout.tsx

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,37 @@ import {
1010

1111
import config from '../config/basic';
1212
import Brand from './component/Brand';
13-
14-
const RightContent: React.FC<{
15-
user?: UserModel;
16-
}> = (props) => {
17-
const user = props?.user;
18-
19-
if (!user) {
20-
return (
21-
<div className="cnode-header-right">
22-
<Button
23-
type="link"
24-
onClick={() => {
25-
history.push('/auth');
26-
}}
27-
>
28-
登录
29-
</Button>
30-
</div>
31-
);
32-
}
33-
34-
const { loginname, avatar_url } = user;
35-
return (
36-
<div className="cnode-header-right">
37-
<Tooltip title={loginname}>
38-
<Avatar shape="square" size="small" src={avatar_url} />
39-
</Tooltip>
40-
</div>
41-
);
42-
};
13+
import RightContent from './component/RightContent';
14+
15+
// const RightContent: React.FC<{
16+
// user?: UserModel;
17+
// }> = (props) => {
18+
// const user = props?.user;
19+
20+
// if (!user) {
21+
// return (
22+
// <div className="cnode-header-right">
23+
// <Button
24+
// type="link"
25+
// onClick={() => {
26+
// history.push('/auth');
27+
// }}
28+
// >
29+
// 登录
30+
// </Button>
31+
// </div>
32+
// );
33+
// }
34+
35+
// const { loginname, avatar_url } = user;
36+
// return (
37+
// <div className="cnode-header-right">
38+
// <Tooltip title={loginname}>
39+
// <Avatar shape="square" size="small" src={avatar_url} />
40+
// </Tooltip>
41+
// </div>
42+
// );
43+
// };
4344

4445
const layoutConfig = ({
4546
initialState,
@@ -90,7 +91,7 @@ const layoutConfig = ({
9091
item.path && <Link to={item.path}>{item.name}</Link>,
9192

9293
rightContentRender: () => {
93-
return <RightContent user={initialState.user} />;
94+
return <RightContent />;
9495
},
9596

9697
footerRender: () => (

0 commit comments

Comments
 (0)