add: 支持博文的列表查看和删除

This commit is contained in:
SunnyQjm committed 2019-10-31 16:02:47 +08:00
1 parent 7c3706b298
commit 6e676fb689
14 files changed
+357 -81

No files matched your search

@@ -15,6 +15,37 @@ interface EditableTagGroupComponentState {
inputValue: string
}
const colors = [
'magenta',
'red',
'volcano',
'orange',
'gold',
'lime',
'green',
'cyan',
'blue',
'geekblue',
'purple'
];
function getHashCode(str: string, caseSensitive: boolean = false) {
if (!caseSensitive) {
str = str.toLowerCase();
}
// 1315423911=b'1001110011001111100011010100111'
let hash = 1315423911, i, ch;
for (i = str.length - 1; i >= 0; i--) {
ch = str.charCodeAt(i);
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return (hash & 0x7FFFFFFF);
}
function getRandColor(tag: string) {
return colors[getHashCode(tag) % colors.length];
}
/**
* 可编辑,带动画的标签组
*/
@@ -87,6 +118,7 @@ class EditableTagGroupComponent extends React.Component<EditableTagGroupComponen
e.preventDefault();
this.handleClose(tag);
}}
color={getRandColor(tag)}
>
{tag}
</Tag>
+7 -7
View File
@@ -28,10 +28,10 @@
}
}
a {
text-decoration: none;
color: #555;
&:hover{
color: #555;
}
}
//a {
// text-decoration: none;
// color: #555;
// &:hover{
// color: #555;
// }
//}
+6
View File
@@ -0,0 +1,6 @@
export interface Comment {
id: number,
name: string,
email: string,
content: string,
}
+8
View File
@@ -4,3 +4,11 @@ export interface BaseResponseBody<T> {
message: string;
data: T;
}
export interface BaseListData<T> {
list: T[],
total: number,
page: number,
size: number,
}
+25 -3
View File
@@ -3,14 +3,19 @@ import {BaseResponseBody} from "@/data/interface/response.interface";
import {StatusCode} from "@/data/network/status-code";
import {User} from "@/data/user";
import router from "umi/router";
import {CreatePostParam} from "@/data/param/request.param";
import {CreatePostParam, DelPostParam, GetPostsParam} from "@/data/param/request.param";
import {message} from "antd";
export const API = {
INFO: {
DEFAULT_PAGE_SIZE: 10,
login: '/login',
register: '/register',
getUserInfo: '/admin/getUserInfo',
createPost: '/admin/create-post',
delPost: 'admin/del-post',
getPosts: '/post/list',
},
global: {
token: ''
@@ -58,12 +63,14 @@ export const API = {
case StatusCode.NEED_ADMIN_PERMISSION:
case StatusCode.COMMENT_ERROR:
case StatusCode.NEED_LOGIN:
message.error(responseData.message);
break;
case StatusCode.UN_AUTHORIZED:
message.warning('请先登录');
router.replace('/login');
break;
}
if(responseData.statusCode === StatusCode.SUCCESS) {
if (responseData.statusCode === StatusCode.SUCCESS) {
return responseData.data;
} else {
return Promise.reject(responseData);
@@ -112,7 +119,22 @@ export const API = {
*/
createPost(createPostParam: CreatePostParam) {
return API.eastPost(API.INFO.createPost, createPostParam);
}
},
/**
* 获取博文列表
* @param getPostsParam
*/
getPosts(getPostsParam: GetPostsParam) {
return API.easyGet(API.INFO.getPosts, getPostsParam);
},
/**
* 删除博文
* @param delPostParam
*/
delPost(delPostParam: DelPostParam) {
return API.eastPost(API.INFO.delPost, delPostParam);
}
};
+13
View File
@@ -3,3 +3,16 @@ export interface CreatePostParam {
content: string,
tags?: string[],
}
export interface GetListParam {
page: number,
size: number
}
export interface GetPostsParam extends GetListParam {
tagId?: string,
}
export interface DelPostParam {
postId: number
}
+7
View File
@@ -1,7 +1,14 @@
import {Tag} from "@/data/tag";
import {Comment} from "@/data/comment";
import {User} from "@/data/user";
export interface Post {
id: number,
title: string,
content: string,
tags: Tag[],
comments: Comment[],
owner: User,
createdAt: number,
updatedAt: number,
}
+4
View File
@@ -0,0 +1,4 @@
export interface Tag {
id: number,
tagName: string,
}
+10 -11
View File
@@ -4,17 +4,14 @@ import {User} from "@/data/user";
import router from 'umi/router';
import {message} from 'antd'
import {LocalStorageKeys, LocalStorageManager} from "@/manager/storage.manager";
import {CreatePostParam} from "@/data/param/request.param";
export interface AdminModelState {
user: User,
loginIng: boolean
}
import {CreatePostParam, DelPostParam, GetPostsParam} from "@/data/param/request.param";
import {Post} from "@/data/post";
export default {
namespace: 'admin',
state: {
user: null,
posts: [],
},
reducers: {
/**
@@ -23,7 +20,7 @@ export default {
* @param action
*/
updateUser(state: any, action: Action<User>) {
if(action.data && action.data.access_token) {
if (action.data && action.data.access_token) {
API.global.token = action.data.access_token;
}
return {
@@ -41,12 +38,14 @@ export default {
...state,
user: null
}
}
},
},
effects: {
* init(action: Action<any>, operators: Operators) {
const user = yield LocalStorageManager.getObj(LocalStorageKeys.USER);
if(user) {
if (user) {
yield operators.put({
type: 'updateUser',
data: user
@@ -79,8 +78,8 @@ export default {
* createPost(action: Action<CreatePostParam>, operators: Operators) {
yield operators.call(API.createPost, action.data);
message.info('发表成功');
}
message.success('发表成功');
},
}
}
+162 -4
View File
@@ -1,14 +1,39 @@
import React from "react";
import Antd, {Divider, message, Table, Popconfirm} from 'antd';
import {connect} from 'dva';
import withRouter from 'umi/withRouter';
import './index.scss'
import RouterTypes from "umi/routerTypes";
import {Post} from "@/data/post";
import {Tag} from "@/data/tag";
import {dateFormat} from "@/utils/date.util";
interface PostManagerPageProps {
interface PostManagerPageProps extends RouterTypes {
dispatch: any,
loading: boolean,
posts: Post[],
total: number,
page: number,
pageSize: number,
}
interface PostManagerPageState {
}
// // rowSelection object indicates the need for row selection
// const rowSelection = {
// onChange: (selectedRowKeys: any, selectedRows: any) => {
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
// },
// getCheckboxProps: (record: any) => ({
// disabled: record.name === 'Disabled User', // Column configuration not to be checked
// name: record.name,
// }),
// };
/**
* 博文管理页面
*/
@@ -17,13 +42,146 @@ class PostManagerPage extends React.Component<PostManagerPageProps, PostManagerP
super(props);
}
componentDidMount(): void {
this.props.dispatch({
type: 'post-manager/getPosts',
data: {
page: 1,
size: this.props.pageSize
}
});
this.handleDelete = this.handleDelete.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
}
/**
* 处理删除博文
*/
handleDelete(postId: number) {
message.loading(`删除: ${postId}`);
this.props.dispatch({
type: 'post-manager/delPost',
data: postId
})
}
handlePageChange(newPage: number, newPageSize: number) {
this.props.dispatch({
type: 'post-manager/getPosts',
data: {
page: newPage,
size: newPageSize
}
})
}
columns = [
{
title: '标题',
key: 'title',
dataIndex: 'title',
render: (text: any) => <a>{text}</a>,
},
{
title: '内容',
key: 'content',
dataIndex: 'content',
render: (content: string) => <span>
{
content.length > 100 ?
content.substr(0, 100)
:
content
}
</span>
},
{
title: '标签',
key: 'tags',
dataIndex: 'tags',
render: (tags: Tag[]) => (
<span>
{tags.map(tag => {
let color = tag.tagName.length > 5 ? 'geekblue' : 'green';
return (
<Antd.Tag color={color} key={tag.id}>
{tag.tagName}
</Antd.Tag>
);
})}
</span>
)
},
{
title: '创建时间',
key: 'createdAt',
dataIndex: 'createdAt',
render: (date: string) => {
return (
<span>{dateFormat("YYYY-mm-dd HH:MM", new Date(date))}</span>
)
}
},
{
title: '更新时间',
ket: 'updatedAt',
dataIndex: 'updatedAt',
render: (date: string) => {
return (
<span>{dateFormat("YYYY-mm-dd HH:MM", new Date(date))}</span>
)
}
},
{
title: '操作',
key: 'action',
render: (text: string, record: Post) => (
<span>
<a>预览</a>
<Divider type="vertical"/>
<a>编辑</a>
<Divider type="vertical"/>
<Popconfirm title='确认删除?' okText='确认' cancelText='取消' onConfirm={() => this.handleDelete(record.id)}>
<a style={{color: 'red'}}>删除</a>
</Popconfirm>
</span>
),
}
];
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
const {
loading,
posts,
page,
pageSize,
total
} = this.props;
const data = posts.map(post => {
return {
...post,
key: post.id
}
});
const pagination: any = {
pageSize,
total: total,
current: page,
onChange: this.handlePageChange
};
return (
<div>
PostManager Page
<Table columns={this.columns} dataSource={data} useFixedHeader loading={loading} pagination={pagination}/>
</div>
);
}
}
export default PostManagerPage
function mapStateToProps(state: any) {
return {
...state['post-manager'],
loading: state.loading.effects['post-manager/getPosts'],
};
}
export default connect(mapStateToProps)(withRouter(PostManagerPage))
@@ -0,0 +1,64 @@
import {Action, Operators} from "@/data/interface/dva.interface";
import {DelPostParam, GetPostsParam} from "@/data/param/request.param";
import {API} from "@/data/network/api";
import {message} from "antd";
import {Post} from "@/data/post";
import {BaseListData} from "@/data/interface/response.interface";
export default {
namespace: 'post-manager',
state: {
posts: [],
total: 0,
page: 1,
pageSize: API.INFO.DEFAULT_PAGE_SIZE
},
reducers: {
updatePosts(state: any, action: Action<BaseListData<Post>>) {
return {
...state,
total: action.data.total,
posts: action.data.list,
page: action.data.page,
}
},
removePost(state: any, action: Action<number>) {
return {
...state,
posts: state.posts.filter((post: Post) => post.id !== action.data)
}
}
},
effects: {
* getPosts(action: Action<GetPostsParam>, operators: Operators) {
const postList = yield operators.call(API.getPosts, action.data);
console.log(postList);
yield operators.put({
type: 'updatePosts',
data: {
...postList,
...action.data,
}
})
},
* delPost(action: Action<DelPostParam>, operators: Operators) {
const result = yield operators.call(API.delPost, {
postId: action.data
});
message.destroy();
if (result) { // 删除成功
yield operators.put({
type: 'removePost',
data: action.data
});
message.success('删除成功')
} else { // 删除失败
message.error('删除失败')
}
},
}
}
-9
View File
@@ -1,9 +0,0 @@
.edit-post-page {
&__title-input {
border: none;
border-radius: 10px;
margin-bottom: 20px;
margin-top: 20px;
}
}
-47
View File
@@ -1,47 +0,0 @@
import React from "react";
import MdEditor from "for-editor";
import {
Input
} from 'antd';
import './index.scss'
interface EditPostPageProps {
}
interface EditPostPageState {
value: string,
}
/**
* 编辑和发布博文的界面
*/
class EditPostPage extends React.Component<EditPostPageProps, EditPostPageState> {
constructor(props: EditPostPageProps) {
super(props);
this.state = {
value: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(value: string) {
this.setState({
value
})
}
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
const {
value
} = this.state;
return (
<div className='edit-post-page'>
<Input placeholder='在此输入文章标题' className='edit-post-page__title-input'/>
<MdEditor value={value} onChange={this.handleChange}/>
</div>
);
}
}
export default EditPostPage
+19
View File
@@ -0,0 +1,19 @@
export const dateFormat = (fmt: any, date: any) => {
let ret;
let opt: any = {
"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"S+": date.getSeconds().toString() // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(ret[1], (ret[1].length === 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
}
}
return fmt;
};