实战react技术栈+express前后端博客项目(8)-- 前端管理界面标签管理+后端对应接口开发...
項目地址:https://github.com/Nealyang/R...
本想等項目做完再連載一波系列博客,隨著開發的進行,也是的確遇到了不少坑,請教了不少人。遂想,何不一邊記錄踩坑,一邊分享收獲呢。分享當然是好的,
如果能做到集思廣益,那豈不是更美。我們的口號是:堅決不會爛尾
本博客為連載代碼博客同步更新博客,隨著項目往后開發可能會遇到前面寫的不合適的地方會再回頭修改。如有不妥~歡迎兄弟們不嗇賜教。謝謝!
前面內容修改部分
由于該博客內容是開發和發文同步連載,所以在隨著開發的進行,會修改之前的開發代碼。
權限認證
對于權限認證之前我們只做了一部分權限管理,在前端頁面發生跳轉的時候,我們通過檢測state的userInfo來確定當前登錄用戶是否有權訪問。
但是,這里存在一個隱患,就是我登錄到一個管理界面以管理員身份,但是知道我身份過期之前都沒進行操作和跳轉,一直在后端管理界面。然后當我身份過期以后,我進行了admin的一些管理操作(增刪改查),且不涉及到頁面的跳轉。前端沒法通過url來判斷。
所以,這里我們修改了前后端內容,攔截所有的/admin的api操作,來判斷身份是否過期。
admin.js
//admin請求后臺驗證router.use( (req,res,next) =>{if(req.session.userInfo){next()}else{res.send(responseClient(res,200,1,'身份信息已過期,請重新登錄'));} });在前端saga里我們需要判斷接口返回的信息。
export function* delTagFlow() {while (true){let req = yield take(ManagerTagsTypes.DELETE_TAG);let res = yield call(delTag,req.name);if (res.code === 0) {yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: res.message, msgType: 1});yield put({type:ManagerTagsTypes.GET_ALL_TAGS});} 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});}}}目前我沒有想到可以一勞永逸不要每個saga都做處理的方法,如果兄弟們有好的想法,望不嗇賜教,提issue,我們一起討論
前端路由抽出組件
之前我們在前端頁面中,front組件直接寫的一個函數,后來發現不是很合理,因為front我需要將他變為容器組件。所以這里我們需要把它抽出來一個class作為container
const {get_all_tags} = actions;class Front extends Component{constructor(props){super(props);}render(){const {url} = this.props.match;return(<div><div className={`${animationStyle.animated} ${animationStyle.fadeInDown}`}><Banner/><Menus categories={this.props.categories} history={this.props.history}/></div><Switch><Route exact path={url} component={Home}/><Route path={`/detail/:id`} component={Detail}/><Route path={`/:tag`} component={Home}/><Route component={NotFound}/></Switch></div>)}componentDidMount() {this.props.get_all_tags();} }Front.defaultProps = {categories:[] };Front.propTypes = {categories:PropTypes.array.isRequired };function mapStateToProps(state) {return{categories:state.admin.tags} } function mapDispatchToProps(dispatch) {return{get_all_tags:bindActionCreators(get_all_tags,dispatch)} } export default connect(mapStateToProps,mapDispatchToProps )(Front)Home.js 路由判斷重定向
return (tags.length>1&&this.props.match.params.tag && (tags.indexOf(this.props.match.params.tag) === -1 || this.props.location.pathname.lastIndexOf('\/') > 0)?<Redirect to='/404'/>:<div className={style.container}><div className={style.contentContainer}><div className={`${style.newsContainer} ${anStyle.animated} ${anStyle.fadeInUp}`}><ArticleList/><div className={style.paginationContainer}><Pagination defaultCurrent={6} total={500}/></div></div><div className={`${style.loginContainer} ${anStyle.animated} ${anStyle.fadeInRight}`}>{this.props.userInfo.userId?<Logined history={this.props.history} userInfo={this.props.userInfo}/>:<Login login={login} register={register}/>}</div></div></div>)這里我們需要判斷tags的長度,因為現在tags是異步獲取的。所以存在時差。比如命名可以訪問/Html標簽,但是由于是異步獲取的tags,當在當前頁面刷新的時候,tags并沒有加載完全,所以會直接重定向到404頁面。
標簽管理運行效果展示
因為gif是在太大了所以這里就放兩張圖片,大家略微感受下。
- 初始狀態
- 添加tag
狀態]
- 前端標簽變化
- 刪除tag
態]
- 回到初始狀態,/Vue刷新會Redirect到404
g)
后端代碼部分
//刪除標簽 router.get('/delTag', function (req, res) {let {name} = req.query;Tags.remove({name}).then(result => {if(result.result.n === 1){responseClient(res,200,0,'刪除成功!')}else{responseClient(res,200,1,'標簽不存在');}}).catch(err => {responseClient(res);}); });//添加標簽 router.post('/addTag', function (req, res) {let {name} = req.body;Tags.findOne({name}).then(result => {if (!result) {let tag = new Tags({name});tag.save().then(data => {responseClient(res, 200, 0, '添加成功', data);}).catch(err => {throw err})} else {responseClient(res, 200, 1, '該標簽已存在');}}).catch(err => {responseClient(res);}); });module.exports = router;為了代碼清晰,方便管理,這里直接就分路由到/tag下。操作很常規,就是刪除和添加標簽。
對于獲取全部標簽,我放到admin外面,因為畢竟前端頁面也需要這個接口。如果都放到/api/admin/getAllTags的話,在/admin請求的時候會進行身份驗證。所以將獲取全部標簽接口放到tags下是不合理的。
這里我們選擇放在main.js中
//獲取全部標簽 router.get('/getAllTags', function (req, res) {Tags.find(null,'name').then(data => {responseClient(res, 200, 0, '請求成功', data);}).catch(err => {responseClient(res);}) });前端部分修改
對于前端組織結構部分的修改上面已經說完了。這里說下saga中的處理
adminTag界面編碼:
class AdminManagerTags extends Component{constructor(props){super(props);this.state={tags: ['首頁', 'HTML', 'CSS','JAVASCRIPT'],inputVisible: false,inputValue: '',}}handleClose = (removedTag) => {//刪除標簽this.props.deleteTag(removedTag)};showInput = () => {this.setState({ inputVisible: true }, () => this.input.focus());};handleInputChange = (e) => {this.setState({ inputValue: e.target.value });};handleInputConfirm = () => {// 添加標簽this.props.addTag(this.state.inputValue);this.setState({inputVisible: false,inputValue: '',});};saveInputRef = input => this.input = input;render(){const { inputVisible, inputValue } = this.state;const {tags} = this.props;return(<div><h2 className={style.titleStyle}>標簽管理</h2>{tags.map((tag, index) => {const isLongTag = tag.length > 20;const tagElem = (<Tag className={style.tagStyle} key={index} closable={index !== 0} afterClose={() => this.handleClose(tag)}>{isLongTag ? `${tag.slice(0, 20)}...` : tag}</Tag>);return isLongTag ? <Tooltip key={tag} title={tag}>{tagElem}</Tooltip> : tagElem;})}{inputVisible && (<InputclassName={style.tagStyle}ref={this.saveInputRef}type="text"size="small"style={{ width: 108 }}value={inputValue}onChange={this.handleInputChange}onBlur={this.handleInputConfirm}onPressEnter={this.handleInputConfirm}/>)}{!inputVisible && <Button className={style.tagStyle} size="small" type="dashed" onClick={this.showInput}>+ New Tag</Button>}</div>)}componentDidMount() {this.props.getAllTags();} }function mapStateToProps(state) {return{tags:state.admin.tags} }function mapDispatchToProps(dispatch) {return{getAllTags : bindActionCreators(get_all_tags,dispatch),deleteTag : bindActionCreators(delete_tag,dispatch),addTag : bindActionCreators(add_tag,dispatch),} }export default connect(mapStateToProps,mapDispatchToProps )(AdminManagerTags)saga的處理:
export function* delTag(name) {yield put({type: IndexActionTypes.FETCH_START});try {return yield call(get, `/admin/tags/delTag?name=${name}`);} catch (err) {yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: '網絡請求錯誤', msgType: 0});} finally {yield put({type: IndexActionTypes.FETCH_END})} } ... ...export function* delTagFlow() {while (true){let req = yield take(ManagerTagsTypes.DELETE_TAG);let res = yield call(delTag,req.name);if (res.code === 0) {yield put({type: IndexActionTypes.SET_MESSAGE, msgContent: res.message, msgType: 1});yield put({type:ManagerTagsTypes.GET_ALL_TAGS});} 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});}} }操作和之前的都沒有兩樣,需要注意的就是這里返回信息我們多判斷了一層用戶信息是否過期以及在saga中的處理。
結束語
至此,標簽管理也基本完事了。對于前端頁面路由的Link還是history.push這里就不做解釋了。大家可以多看看代碼。
下一篇我們將進行文章的操作的。發文,增刪改查等功能。
項目實現步驟系列博客
- 實戰react技術棧+express前后端博客項目(0)-- 預熱一波
- 實戰react技術棧+express前后端博客項目(1)-- 整體項目結構搭建、state狀態樹設計
- 實戰react技術棧+express前后端博客項目(2)-- 前端react-xxx、路由配置
- 實戰react技術棧+express前后端博客項目(3)-- 后端路由、代理以及靜態資源托管等其他配置說明
- 實戰react技術棧+express前后端博客項目(4)-- 博客首頁代碼編寫以及redux-saga組織
- 實戰react技術棧+express前后端博客項目(5)-- 前后端實現登錄功能
- 實戰react技術棧+express前后端博客項目(6)-- 使用session實現免登陸+管理后臺權限驗證
- 實戰react技術棧+express前后端博客項目(7)-- 前端管理界面用戶查看功能+后端對應接口開發
- 實戰react技術棧+express前后端博客項目(8)-- 前端管理界面標簽管理功能+后端對應接口開發
- 實戰react技術棧+express前后端博客項目(9)-- 前端管理界面評論管理功能+后端對應接口開發
- 實戰react技術棧+express前后端博客項目(10)-- 前端管理界面發表文章功能
- 實戰react技術棧+express前后端博客項目(11)-- 后端接口對應文章部分的增刪改查
- 實戰react技術棧+express前后端博客項目(12)-- 前端對于發文部分的完善(增刪改查、分頁等)
- 實戰react技術棧+express前后端博客項目(13)-- 前端對于發文部分的完善(增刪改查等)
- 實戰react技術棧+express前后端博客項目(14)-- 內容詳情頁以及閱讀數的展示
- 實戰react技術棧+express前后端博客項目(15)-- 博客添加評論功能以及對應后端實現
- 實戰react技術棧+express前后端博客項目(16)-- pm2 的使用說明
- 實戰react技術棧+express前后端博客項目(17)-- 收工
## 交流
倘若有哪里說的不是很明白,或者有什么需要與我交流,歡迎各位提issue。或者加群聯系我~
掃碼關注我的個人微信公眾號,直接回復,必有回應。分享更多原創文章。點擊交流學習加我微信、qq群。一起學習,一起進步
---
歡迎兄弟們加入:
Node.js技術交流群:209530601
React技術棧:398240621
前端技術雜談:604953717 (新建)
---
總結
以上是生活随笔為你收集整理的实战react技术栈+express前后端博客项目(8)-- 前端管理界面标签管理+后端对应接口开发...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Springboot如何利用http请求
- 下一篇: 2017年html5行业报告,云适配发布