Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ const routes: IRoute[] = [
name: '主页',
component: '@/page/topic',
},
{
path: '/my/messages',
exact: true,
icon: 'message',
name: '未读消息',
access: 'canReadMessage',
component: '@/page/message',
},
{
path: '/about',
exact: true,
icon: 'info',
name: '关于',
name: '关于我们',
component: '@/page/about',
},
{
Expand Down
1 change: 1 addition & 0 deletions src/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export default function (initialState: InitialState) {
return {
canPostTopic: !!token,
canPostComment: !!token,
canReadMessage: !!token,
};
}
27 changes: 17 additions & 10 deletions src/component/Brand/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import React from 'react';
import React, { useContext } from 'react';
import { RouteContext } from '@ant-design/pro-layout';

import * as styles from './index.less';

const Brand: React.FC<Props> = ({ logo, title, description }) => (
<div className={styles.container}>
<img className={styles.logo} src={logo} alt="logo" />
{/* <h1 className={styles.title}>{title}</h1> */}
<p className={styles.description}>{description}</p>
</div>
);
const Brand: React.FC<Props> = ({ logo, title, description }) => {
const { collapsed, isMobile } = useContext(RouteContext);

return (
<div className={styles.container}>
<img className={styles.logo} src={logo} alt="logo" />
{collapsed || isMobile ? null : (
<p className={styles.description}>{description}</p>
)}
</div>
);
};

export default Brand;

interface Props {
title: string;
description: string;
logo?: string;
title?: string;
description: string;
}
80 changes: 80 additions & 0 deletions src/component/MessageList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import dayjs from 'dayjs';
import { useHistory } from 'umi';
import { Space, Avatar, Tag } from 'antd';
import { ListToolBarProps } from '@ant-design/pro-table';
import ProList, { ProListMetas } from '@ant-design/pro-list';

import { MESSAGE_TYPE_MAP, MessageType } from '@/constants';

import * as styles from './index.less';

const MessageList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
const history = useHistory();

const metas: ProListMetas = {
avatar: {
dataIndex: 'author.avatar_url',
render: (_, entity: MessageModel) => {
const { type: _type, author } = entity;
const type = MESSAGE_TYPE_MAP[_type as MessageType];

return (
<Space size={16}>
<div
style={{
width: '200px',
}}
>
<Space size={8}>
<Avatar size="small" src={author.avatar_url} />
<span>{author.loginname}</span>
</Space>
</div>

<Tag color={type.color}>{type.name}</Tag>
</Space>
);
},
},
title: {
dataIndex: 'title',
valueType: 'text',
render: (_, entity: MessageModel) => {
return entity.topic.title;
},
},
actions: {
render: (_, entity: MessageModel) => {
return dayjs(entity.create_at).fromNow();
},
},
};

return (
<ProList
rowKey="id"
showActions="always"
dataSource={dataSource}
loading={loading}
metas={metas}
className={styles.list}
toolbar={toolbar}
onRow={(record: MessageModel) => {
return {
onClick: () => {
history.push(`/topic/${record.topic.id}`);
},
};
}}
/>
);
};

export default MessageList;

interface Props {
dataSource?: MessageModel[];
loading?: boolean;
toolbar?: ListToolBarProps;
}
67 changes: 67 additions & 0 deletions src/component/RightContent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import { history, useModel, Link } from 'umi';
import { Avatar, Space, Button, Badge, Menu, Dropdown } from 'antd';

const RightContent: React.FC<Props> = (props) => {
const { user, logout } = useModel('user');
const { count } = useModel('message');

if (!user) {
return (
<div className="cnode-header-right">
<Button
type="link"
onClick={() => {
history.push('/auth');
}}
>
登录
</Button>
</div>
);
}

const { loginname, avatar_url } = user;

const menu = (
<Menu>
<Menu.Item key="profile">
<Link to={`/user/${loginname}`}>个人资料</Link>
</Menu.Item>
<Menu.Item key="message">
<Badge count={count} size="small">
<Link to="/my/messages">未读消息</Link>
</Badge>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="logout">
<Link
to="/"
onClick={(e) => {
e.preventDefault();
logout();
}}
>
退出登录
</Link>
</Menu.Item>
</Menu>
);

return (
<div className="cnode-header-right">
<Dropdown overlay={menu}>
<Badge count={count} size="small">
<Space size={8}>
<Avatar shape="square" size="small" src={avatar_url} />
<span>{loginname}</span>
</Space>
</Badge>
</Dropdown>
</div>
);
};

export default RightContent;

interface Props {}
10 changes: 10 additions & 0 deletions src/component/TopicList/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.list {
:global {
.ant-card {
padding: 0;
> .ant-card-body {
padding: 0;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { TABS_MAP, TabType } from '@/constants';
import ProList, { ProListMetas } from '@ant-design/pro-list';
import { Space, Avatar, Tag } from 'antd';
import React from 'react';
import dayjs from 'dayjs';
import { useHistory } from 'umi';
import { Space, Avatar, Tag } from 'antd';
import { ListToolBarProps } from '@ant-design/pro-table';
import ProList, { ProListMetas } from '@ant-design/pro-list';

import { TABS_MAP, TabType } from '@/constants';

import * as styles from './index.less';

const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
const TopicList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
const history = useHistory();

const metas: ProListMetas = {
avatar: {
dataIndex: 'author.avatar_url',
render: (_, entity) => {
render: (_, entity: TopicModel) => {
const { tab: _tab, author, reply_count, visit_count, top } = entity;

const category = TABS_MAP[_tab as TabType];
Expand Down Expand Up @@ -55,7 +57,7 @@ const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
valueType: 'text',
},
actions: {
render: (_, entity) => {
render: (_, entity: TopicModel) => {
const { last_reply_at } = entity;
return dayjs(last_reply_at).fromNow();
},
Expand All @@ -82,10 +84,10 @@ const TopicItemList: React.FC<Props> = ({ dataSource, loading, toolbar }) => {
);
};

export default TopicItemList;
export default TopicList;

interface Props {
dataSource?: any[];
dataSource?: TopicModel[];
loading?: boolean;
toolbar?: ListToolBarProps;
}
15 changes: 12 additions & 3 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ export const TABS_MAP = {
name: '客户端测试',
color: 'green',
},
dev: {
name: '客户端测试',
};

export type TabType = keyof typeof TABS_MAP;

export const MESSAGE_TYPE_MAP = {
at: {
name: '提到了你',
color: '#108ee9',
},
reply: {
name: '回复了你',
color: 'green',
},
};

export type TabType = keyof typeof TABS_MAP;
export type MessageType = keyof typeof MESSAGE_TYPE_MAP;

export enum FORM_TYPE {
LOGIN = 'login',
Expand Down
63 changes: 32 additions & 31 deletions src/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,37 @@ import {

import config from '../config/basic';
import Brand from './component/Brand';

const RightContent: React.FC<{
user?: UserModel;
}> = (props) => {
const user = props?.user;

if (!user) {
return (
<div className="cnode-header-right">
<Button
type="link"
onClick={() => {
history.push('/auth');
}}
>
登录
</Button>
</div>
);
}

const { loginname, avatar_url } = user;
return (
<div className="cnode-header-right">
<Tooltip title={loginname}>
<Avatar shape="square" size="small" src={avatar_url} />
</Tooltip>
</div>
);
};
import RightContent from './component/RightContent';

// const RightContent: React.FC<{
// user?: UserModel;
// }> = (props) => {
// const user = props?.user;

// if (!user) {
// return (
// <div className="cnode-header-right">
// <Button
// type="link"
// onClick={() => {
// history.push('/auth');
// }}
// >
// 登录
// </Button>
// </div>
// );
// }

// const { loginname, avatar_url } = user;
// return (
// <div className="cnode-header-right">
// <Tooltip title={loginname}>
// <Avatar shape="square" size="small" src={avatar_url} />
// </Tooltip>
// </div>
// );
// };

const layoutConfig = ({
initialState,
Expand Down Expand Up @@ -90,7 +91,7 @@ const layoutConfig = ({
item.path && <Link to={item.path}>{item.name}</Link>,

rightContentRender: () => {
return <RightContent user={initialState.user} />;
return <RightContent />;
},

footerRender: () => (
Expand Down
Loading