項目地址:github.com/Nealyang/Re…前端
本想等項目作完再連載一波系列博客,隨着開發的進行,也是的確遇到了很多坑,請教了很多人。遂想,何不一邊記錄踩坑,一邊分享收穫呢。分享固然是好的, 若是能作到集思廣益,那豈不是更美。咱們的口號是:堅定不會爛尾react
本博客爲連載代碼博客同步更新博客,隨着項目日後開發可能會遇到前面寫的不合適的地方會再回頭修改。若有不妥~歡迎兄弟們不嗇賜教。謝謝!git
因爲該博客內容是開發和發文同步連載,因此在隨着開發的進行,會修改以前的開發代碼。github
對於權限認證以前咱們只作了一部分權限管理,在前端頁面發生跳轉的時候,咱們經過檢測state的userInfo來肯定當前登陸用戶是否有權訪問。express
可是,這裏存在一個隱患,就是我登陸到一個管理界面以管理員身份,可是知道我身份過時以前都沒進行操做和跳轉,一直在後端管理界面。而後當我身份過時之後,我進行了admin的一些管理操做(增刪改查),且不涉及到頁面的跳轉。前端無法經過url來判斷。redux
因此,這裏咱們修改了先後端內容,攔截全部的/admin的api操做,來判斷身份是否過時。後端
admin.jsapi
//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)
複製代碼
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是在太大了因此這裏就放兩張圖片,你們略微感覺下。
//刪除標籤
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 && (
<Input
className={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這裏就不作解釋了。你們能夠多看看代碼。
下一篇咱們將進行文章的操做的。發文,增刪改查等功能。
歡迎關注我的微信公衆號: Nealyang 全棧前端,獲取第一手文章推送和免費全棧電子書分享福利