項目地址:https://github.com/Nealyang/R...前端
本想等項目作完再連載一波系列博客,隨着開發的進行,也是的確遇到了很多坑,請教了很多人。遂想,何不一邊記錄踩坑,一邊分享收穫呢。分享固然是好的,
若是能作到集思廣益,那豈不是更美。咱們的口號是:堅定不會爛尾react
本博客爲連載代碼博客同步更新博客,隨着項目日後開發可能會遇到前面寫的不合適的地方會再回頭修改。若有不妥~歡迎兄弟們不嗇賜教。謝謝!git
文章內容初定包含以下字段:文章標題、文章內容、做者、文章標籤、瀏覽數、評論數、發表時間、是否發佈github
因此定義schema以下:數據庫
import mongoose from 'mongoose' module.exports = new mongoose.Schema({ title:String,//文章標題 content:String,//文章內容 viewCount:Number,//瀏覽次數 commentCount:Number,//評論次數 time:String,//發表時間 coverImg:String,//封面圖片 author:String,//做者 tags:Array,//標籤 isPublish:Boolean//是否發佈 });
後端發文接口開發其實就是一個存儲文章的接口,初步接口設計爲/api/admin/article/addArticleexpress
router.post('/addArticle', function (req, res) { const { title, content, time, tags, isPublish } = req.body; const author = req.session.userInfo.username; const coverImg = `/${Math.round(Math.random() * 9 + 1)}.jpg`; const viewCount = 0; const commentCount = 0; let tempArticle = new Article({ title, content, isPublish, viewCount, commentCount, time, author, coverImg, tags }); tempArticle.save().then(data=>{ responseClient(res,200,0,'保存成功',data) }).cancel(err=>{ console.log(err); responseClient(res); }); });
後端都比較常規。對於路由設計以及model你們能夠自行查看源碼redux
界面編碼:後端
render() { return ( <div> <h2>發文</h2> <div className={style.container}> <span className={style.subTitle}>標題</span> <Input className={style.titleInput} placeholder={'請輸入文章標題'} type='text' value={this.props.title} onChange={this.titleOnChange.bind(this)}/> <span className={style.subTitle}>正文</span> <textarea className={style.textArea} value={this.props.content} onChange={this.onChanges.bind(this)}/> <span className={style.subTitle}>分類</span> <Select mode="multiple" className={style.titleInput} placeholder="請選擇分類" onChange={this.selectTags.bind(this)} defaultValue={this.props.tags} > { this.props.tagsBase.map((item) => { return ( <Option key={item}>{item}</Option> ) }) } </Select> <div className={style.bottomContainer}> <Button type="primary" onClick={this.publishArticle.bind(this)} className={style.buttonStyle}>發佈</Button> <Button type="primary" onClick={this.saveArticle.bind(this)} className={style.buttonStyle}>保存</Button> <Button type="primary" onClick={this.preView.bind(this)} className={style.buttonStyle}>預覽</Button> </div> </div> <Modal visible={this.state.modalVisible} title="文章預覽" onOk={this.handleOk.bind(this)} width={'900px'} onCancel={this.handleOk.bind(this)} footer={null} > <div className={style.modalContainer}> <div id='preview' className={style.testCode}> {remark().use(reactRenderer).processSync(this.props.content).contents} </div> </div> </Modal> </div> ) }
因爲定義爲技術博客,因此這裏咱們只支持md語法。使用remark-react插件將md語法轉換。textArea做爲輸入框。目前沒有支持圖片上傳。如若想一想支持圖片上傳功能,請查看我github上另外一個demo。這裏就不作演示了。api
對於發文部分,我單獨存儲了title,tags,content。爲了方便用戶文章在寫到通常的時候,切換別的菜單項。因此將他存儲在state。在input中輸入title,content,tags的時候,直接更新到state中。這樣,在用戶切換到別的tab再切換回來的時候,依舊能夠看到本身以前輸入的內容。微信
const initialState={ title:'', content:'', tags:[] }; export const actionTypes = { UPDATING_TITLE:"UPDATING_TITLE", UPDATING_CONTENT:"UPDATING_CONTENT", UPDATING_TAGS:"UPDATING_TAGS", SAVE_ARTICLE:"SAVE_ARTICLE" }; export const actions = { update_title:function (title) { return{ type:actionTypes.UPDATING_TITLE, title } }, update_content:function (content) { return{ type:actionTypes.UPDATING_CONTENT, content } }, update_tags:function (tags) { return{ type:actionTypes.UPDATING_TAGS, tags } }, save_article:function (data) { return{ type:actionTypes.SAVE_ARTICLE, data } } };
export function reducer(state=initialState,action) { switch (action.type){ case actionTypes.UPDATING_TITLE: return{ ...state,title:action.title }; case actionTypes.UPDATING_CONTENT: return{ ...state,content:action.content }; case actionTypes.UPDATING_TAGS: return{ ...state,tags:action.tags }; default: return state; } }
saga中,咱們須要判斷用戶是保存仍是發佈。因此咱們加了isPublish字段來區分。當爲發佈的時候,一些必填字段須要咱們去判斷。
export function* saveArticleFlow () { while (true){ let request = yield take(NewArticleActionTypes.SAVE_ARTICLE); console.log(request); if(request.data.isPublish){ if(request.data.title === ''){ yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: '請輸入文章標題', msgType: 0}); }else if(request.data.content === ""){ yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: '請輸入文章內容', msgType: 0}); }else if(request.data.tags.length === 0){ yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: '請選擇文章分類', msgType: 0}); } } if((request.data.title&&request.data.content&&request.data.tags.length>0&&request.data.isPublish)|| (!request.data.isPublish)){ let res = yield call(saveArticle,request.data); if(res){ if (res.code === 0) { yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: res.message, msgType: 1}); setTimeout(function () { location.replace('/admin/managerArticle'); }, 1000); } else if (res.message === '身份信息已過時,請從新登陸') { yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: res.message, msgType: 0}); setTimeout(function () { location.replace('/'); }, 1000); } else { yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: res.message, msgType: 0}); } } } } }
當文章發表成功後,咱們這裏給定一秒後,跳轉到文章管理界面,因爲管理界面目前沒有開發,因此如今跳轉到管理界面後,是404.下一篇,咱們將介紹文章管理部分的功能開發。
文章的預覽,直接使用antd的modal,而後轉換md語法,展現效果。
整體來講,文章發佈也是比較簡單。一些細節,還但願你們多多琢磨。至此,一個博客網站的核心功能就完成了。
## 交流
假若有哪裏說的不是很明白,或者有什麼須要與我交流,歡迎各位提issue。或者加羣聯繫我~
掃碼關注個人我的微信公衆號,直接回復,必有迴應。分享更多原創文章。點擊交流學習加我微信、qq羣。一塊兒學習,一塊兒進步
---
歡迎兄弟們加入:
Node.js技術交流羣:209530601
React技術棧:398240621
前端技術雜談:604953717 (新建)
---