1
0
mirror of https://github.com/SunnyQjm/sunny-blog.git synced 2026-06-03 08:16:45 +08:00

add: 接入发表博客的接口

This commit is contained in:
SunnyQjm
2019-10-30 20:24:19 +08:00
parent 3973bfec20
commit 7c3706b298
11 changed files with 279 additions and 18 deletions
+1
View File
@@ -19,6 +19,7 @@
"highlight.js": "^9.15.10",
"mockjs": "^1.1.0",
"node-sass": "^4.13.0",
"rc-tween-one": "^2.6.5",
"react": "^16.8.6",
"react-addons-shallow-compare": "^15.6.2",
"react-dom": "^16.8.6",
+147
View File
@@ -0,0 +1,147 @@
import React from "react";
import {
Tag, Input, Icon
} from 'antd';
import {TweenOneGroup} from 'rc-tween-one';
import './index.scss';
interface EditableTagGroupComponentProps {
onTagsChange: (tags: string[]) => void
}
interface EditableTagGroupComponentState {
tags: string[],
inputVisible: boolean,
inputValue: string
}
/**
* 可编辑,带动画的标签组
*/
class EditableTagGroupComponent extends React.Component<EditableTagGroupComponentProps, EditableTagGroupComponentState> {
static defaultProps = {
onTagsChange: () => {
}
};
constructor(props: EditableTagGroupComponentProps) {
super(props);
this.state = {
tags: [],
inputVisible: false,
inputValue: ''
};
this.handleClose = this.handleClose.bind(this);
this.showInput = this.showInput.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputConfirm = this.handleInputConfirm.bind(this);
this.saveInputRef = this.saveInputRef.bind(this);
this.forMap = this.forMap.bind(this);
}
input?: Input;
/**
* 处理标签移除
* @param removeTag
*/
handleClose(removeTag: string) {
const tags = this.state.tags.filter(tag => tag !== removeTag);
this.props.onTagsChange && this.props.onTagsChange(tags);
this.setState({tags});
}
showInput() {
this.setState({
inputVisible: true
}, () => this.input && this.input.focus());
}
handleInputChange(e: any) {
this.setState({inputValue: e.target.value})
}
handleInputConfirm() {
const {inputValue} = this.state;
let {tags} = this.state;
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue];
}
this.props.onTagsChange && this.props.onTagsChange(tags);
this.setState({
tags,
inputValue: '',
inputVisible: false
})
}
saveInputRef(input: Input) {
this.input = input;
}
forMap(tag: string) {
const tagElem = (
<Tag
closable
onClose={(e: any) => {
e.preventDefault();
this.handleClose(tag);
}}
>
{tag}
</Tag>
);
return (
<span key={tag} style={{display: 'inline-block'}}>
{tagElem}
</span>
);
}
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
const {tags, inputVisible, inputValue} = this.state;
const tagChild = tags.map(this.forMap);
return (
<div>
<div style={{marginBottom: 16}}>
<TweenOneGroup
enter={{
scale: 0.8,
opacity: 0,
type: 'from',
duration: 100,
onComplete: (e: any) => {
e.target.style = '';
},
}}
leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
appear={false}
>
{tagChild}
{inputVisible && (
<Input
ref={this.saveInputRef}
type="text"
size="small"
style={{width: 78}}
value={inputValue}
onChange={this.handleInputChange}
onBlur={this.handleInputConfirm}
onPressEnter={this.handleInputConfirm}
/>
)}
{!inputVisible && (
<Tag onClick={this.showInput} style={{background: '#fff', borderStyle: 'dashed'}}>
<Icon type="plus"/>
</Tag>
)}
</TweenOneGroup>
</div>
</div>
);
}
}
export default EditableTagGroupComponent;
+3 -1
View File
@@ -1,9 +1,11 @@
import NavBarComponent from "@/components/nav-bar";
import PostItemComponent from "@/components/post-item";
import CodeRenderComponent from "@/components/code-render";
import EditableTagGroupComponent from "@/components/editable-tag-group";
export {
NavBarComponent,
PostItemComponent,
CodeRenderComponent
CodeRenderComponent,
EditableTagGroupComponent
};
+14 -2
View File
@@ -3,12 +3,14 @@ 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";
export const API = {
INFO: {
login: '/login',
register: '/register',
getUserInfo: '/admin/getUserInfo',
createPost: '/admin/create-post',
},
global: {
token: ''
@@ -46,11 +48,9 @@ export const API = {
if (requestPromise) {
return requestPromise
.then((result: any) => {
console.log(result);
return result.data;
})
.then((responseData: BaseResponseBody<T>) => {
console.log(responseData);
// 同意处理状态码
switch (responseData.statusCode) {
case StatusCode.SUCCESS:
@@ -100,6 +100,18 @@ export const API = {
*/
getUserInfo: (): Promise<User> => {
return API.easyGet<User>(API.INFO.getUserInfo);
},
///////////////////////////////////////////////////////////////
////////// 管理相关
///////////////////////////////////////////////////////////////
/**
* 创建博客
* @param createPostParam
*/
createPost(createPostParam: CreatePostParam) {
return API.eastPost(API.INFO.createPost, createPostParam);
}
};
+5
View File
@@ -0,0 +1,5 @@
export interface CreatePostParam {
title: string,
content: string,
tags?: string[],
}
+3 -3
View File
@@ -42,9 +42,9 @@ class BasicLayout extends React.Component<BasicLayoutProps, BasicLayoutState> {
componentDidMount(): void {
console.log('global layout mount');
this.props.dispatch({
type: 'admin/init'
});
// this.props.dispatch({
// type: 'admin/init'
// });
}
componentDidUpdate(prevProps: any) {
+10 -4
View File
@@ -2,8 +2,9 @@ import {Action, Operators} from "@/data/interface/dva.interface";
import {API} from "@/data/network/api";
import {User} from "@/data/user";
import router from 'umi/router';
import {} from 'antd'
import {message} from 'antd'
import {LocalStorageKeys, LocalStorageManager} from "@/manager/storage.manager";
import {CreatePostParam} from "@/data/param/request.param";
export interface AdminModelState {
user: User,
@@ -22,6 +23,9 @@ export default {
* @param action
*/
updateUser(state: any, action: Action<User>) {
if(action.data && action.data.access_token) {
API.global.token = action.data.access_token;
}
return {
...state,
user: action.data
@@ -43,7 +47,6 @@ export default {
* init(action: Action<any>, operators: Operators) {
const user = yield LocalStorageManager.getObj(LocalStorageKeys.USER);
if(user) {
API.global.token = user.access_token;
yield operators.put({
type: 'updateUser',
data: user
@@ -59,7 +62,6 @@ export default {
},
* getUserInfo(action: Action<any>, operators: Operators) {
const user = yield operators.call(API.getUserInfo);
API.global.token = user.access_token;
yield operators.put({
type: 'updateUser',
data: user
@@ -67,13 +69,17 @@ export default {
},
* login(action: Action<any>, operators: Operators) {
const user: User = yield operators.call(API.login, action.data.username, action.data.password);
API.global.token = user.access_token;
yield operators.put({
type: 'updateUser',
data: user
});
LocalStorageManager.save(LocalStorageKeys.USER, user);
router.replace('/admin');
},
* createPost(action: Action<CreatePostParam>, operators: Operators) {
yield operators.call(API.createPost, action.data);
message.info('发表成功');
}
}
}
+5 -1
View File
@@ -38,7 +38,11 @@ class AdminLayout extends React.Component<AdminLayoutProps, AdminLayoutState> {
this.onCollapse = this.onCollapse.bind(this);
this.onMenuSelected = this.onMenuSelected.bind(this);
}
componentDidMount(): void {
this.props.dispatch({
type: 'admin/init'
});
}
onCollapse(collapsed: boolean) {
this.setState({collapsed});
+17
View File
@@ -0,0 +1,17 @@
.admin-edit-page {
min-height: 600px;
&__operators {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-bottom: 20px;
}
&__title-input {
margin-bottom: 20px;
}
&__markdown-editor {
height: 100%;
}
}
+74 -7
View File
@@ -1,29 +1,96 @@
import React from "react";
import React, {ChangeEvent} from "react";
import './index.scss'
import {Input, Button} from "antd";
import MdEditor from "for-editor";
import withRouter from 'umi/withRouter';
import {connect} from 'dva';
import {
EditableTagGroupComponent
} from '../../../components';
import {RouterTypes} from "umi";
interface EditPostPageProps {
interface EditPostPageProps extends RouterTypes {
dispatch: any,
loading: boolean
}
interface EditPostPageState {
content: string,
title: string,
tags: string[],
}
/**
* 博客发表页面
*/
class EditPostPage extends React.Component<EditPostPageProps, EditPostPageState> {
static defaultProps = {
loading: false
};
constructor(props: EditPostPageProps) {
super(props);
this.state = {
content: '',
title: '',
tags: [],
};
this.handleMarkdownChange = this.handleMarkdownChange.bind(this);
this.handleTitleChange = this.handleTitleChange.bind(this);
this.handleTagsChange = this.handleTagsChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleMarkdownChange(content: string) {
this.setState({
content
})
}
handleTitleChange(e: any) {
this.setState({
title: e.target.value
});
}
handleTagsChange(tags: string[]) {
this.setState({
tags
})
}
handleSubmit() {
this.props.dispatch({
type: 'admin/createPost',
data: this.state
});
}
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
const {
content,
title
} = this.state;
const {
loading
} = this.props;
return (
<div>
EditPost Page
<div className='admin-edit-page'>
<div className='admin-edit-page__operators'>
<Button type='primary' onClick={this.handleSubmit} loading={loading}></Button>
</div>
<Input placeholder='在此输入文章标题' className='admin-edit-page__title-input' value={title}
onChange={this.handleTitleChange}/>
<EditableTagGroupComponent onTagsChange={this.handleTagsChange}/>
<MdEditor value={content} onChange={this.handleMarkdownChange}/>
</div>
);
}
}
export default EditPostPage
function mapStateToProps(state: any) {
return {
loading: state.loading.effects['admin/createPost']
};
}
export default connect(mapStateToProps)(withRouter(EditPostPage))