mirror of
https://github.com/SunnyQjm/sunny-blog.git
synced 2026-06-03 08:16:45 +08:00
add: 接入发表博客的接口
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface CreatePostParam {
|
||||
title: string,
|
||||
content: string,
|
||||
tags?: string[],
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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('发表成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user