【招聘App】—— React/Nodejs/MongoDB全栈项目:信息完善&用户列表
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅。最终成果github地址:https://github.com/66Web/react-antd-zhaoping,欢迎star。
一、信息完善
boss信息完善页前端实现
- container目录下:创建bossinfo组件目录,使用actd-mobile实现信息输入列表
import React from 'react'
import {NavBar, InputItem, TextareaItem, Button} from 'antd-mobile'
import AvatarSelector from '../../component/avatar-selector/avatar-selector' class BossInfo extends React.Component{
constructor(props){
super(props)
this.state = {
title: ''
}
}
onChange(key, val){
this.setState({
[key]: val
})
}
render(){
return (
<div>
<NavBar mode="dark">Boss完善信息页面</NavBar>
<AvatarSelector
selectAvatar={(imgname) => {
this.setState({
avatar: imgname
})
}}
></AvatarSelector>
<InputItem onChange={(v) => this.onChange('title', v)}>
招聘职位
</InputItem>
<InputItem onChange={(v) => this.onChange('company', v)}>
公司名称
</InputItem>
<InputItem onChange={(v) => this.onChange('money', v)}>
职位薪资
</InputItem>
<TextareaItem
rows={3}
title="职位要求"
autoHeight
onChange={(v) => this.onChange('desc', v)}
>
</TextareaItem>
<Button type="primary">保存</Button>
</div>
)
}
} export default BossInfo component目录下:创建avatar-selector选择头像组件目录
import React from 'react'
import {Grid, List} from 'antd-mobile' class AvatarSelector extends React.Component{
constructor(props){
super(props)
this.state = {}
}
render(){
const avatarList = 'boy,girl,man,woman,bull,chick,crab,hedgehog,hippopotamus,koala,lemur,pig,tiger,whale,zebra'
.split(',')
.map(v=>({
icon:require(`../img/${v}.png`),
text:v
}))
const gridHeader = this.state.text
? (<div>
<span>已选择头像</span>
<img style={{width: 20}} src={this.state.icon}/>
</div>)
: <div>'请选择头像'</div>
return (
<div>
<List renderHeader={() => gridHeader}>
<Grid
data={avatarList}
columnNum={5}
onClick={elm => {
this.setState(elm)
this.props.selectAvatar(elm.text)
}}
/>
</List>
</div>
)
}
} export default AvatarSelector
boss信息完善页后端实现
- user.redux.js中:添加authSuccess验证成功相关的reducer和action
- authSuccess同步action:代替registerSuccess和loginSuccess,它们执行的是相同操作
//action type
const AUTH_SUCCESS = 'AUTH_SUCCESS' //验证成功 //reducer中替换
case AUTH_SUCCESS:
return {...state, msg:'', redirectTo:getRedirectPath(action.payload), ...action.payload} //同步action
function authSuccess(obj){
const {pwd,...data} = obj
return {type: AUTH_SUCCESS, payload:data}
} - 定义update异步action:完善信息页提交后调用服务端/user/update接口,执行更新操作
export function update(data){
return dispatch=>{
axios.post('/user/update', data)
.then(res=>{
if (res.status==200&&res.data.code===0) {
dispatch(authSuccess(res.data.data))
}else{
dispatch(errorMsg(res.data.msg))
}
})
}
}
user.js中:判断cookie中是否存储了userid,若有则根据userid查找并更新用户信息文档(findByIdAndUpdate)
//更新信息
Router.post('/update', function(req, res){
const userid = req.cookies.userid
if(!userid){
return json.dumps({code:1})
}
const body = req.body
User.findByIdAndUpdate(userid, body, function(err, doc){
const data = Object.assign({},{
user: doc.user,
type: doc.type,
}, body)
return res.json({code:0, data})
})
})bossinfo.js中:使用connect连接组件和redux
点击保存按钮时,调用redux中的update异步action更新数据
import {connect} from 'react-redux'
import {update} from '../../redux/user.redux'
import {Redirect} from 'react-router-dom' @connect(
state => state.user,
{update}
) <Button
onClick={()=>{
this.props.update(this.state)
}}
type="primary"
>保存</Button>判断props中redirect是否存在,且不等于当前路由即(location.pathname)时执行跳转
const path = this.props.location.pathname
const redirect = this.props.redirectTo
return (
<div>
{redirect&&redirect!==path ? <Redirect to={this.props.redirectTo}/> : null}
牛人信息完善
- geniusinfo.js中:同bossinfo.js共用一套逻辑
import React from 'react'
import {NavBar, InputItem, TextareaItem, Button} from 'antd-mobile'
import AvatarSelector from '../../component/avatar-selector/avatar-selector'
import {connect} from 'react-redux'
import {update} from '../../redux/user.redux'
import {Redirect} from 'react-router-dom' @connect(
state => state.user,
{update}
)
class GeniusInfo extends React.Component{
constructor(props){
super(props)
this.state = {
title: '',
desc: ''
}
}
onChange(key, val){
this.setState({
[key]: val
})
}
render(){
const path = this.props.location.pathname
const redirect = this.props.redirectTo
return (
<div>
{redirect&&redirect!==path ? <Redirect to={this.props.redirectTo}/> : null}
<NavBar mode="dark">牛人完善信息页面</NavBar>
<AvatarSelector
selectAvatar={(imgname) => {
this.setState({
avatar: imgname
})
}}
></AvatarSelector>
<InputItem onChange={(v) => this.onChange('title', v)}>
求职职位
</InputItem>
<TextareaItem
rows={3}
title="个人简介"
autoHeight
onChange={(v) => this.onChange('desc', v)}
>
</TextareaItem>
<Button
onClick={()=>{
this.props.update(this.state)
}}
type="primary"
>保存</Button>
</div>
)
}
} export default GeniusInfo
组件属性类型校验
- PropTypes提供了多种验证器:react15.5之前是封装在react中的,react16之后分离出来,需要另外安装
npm install prop-types --save
JavaScript基础数据类型,包括数组、布尔、函数、数字、对象、字符串
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string
如果不能为空
isRequiredavatar-selector.js中:指定组件属性类型为函数,且不能为空
import PropTypes from 'prop-types' static propTypes = {
selectAvatar: PropTypes.func.isRequired
}指定数据类型成数组
组件名称:PropTypes.arrayOf(PropTypes.number)
指定数据类型到对象
组件名称:PropTypes.objectOf(PropTypes.number)
二、用户列表
应用骨架
- index.js中:添加路由组件DashBoard
- 不设置path,则当路由匹配不到对应组件时,都会跳转到DashBoard组件中
- 使用DashBoard组件同时管理四个路由:/boss,/genius,/msg,/me
import DashBoard from './component/dashboard/dashboard' <Route component={DashBoard}></Route>//一定要放在<Switch>内容的最下方
导航和跳转
- component目录下:创建dashboard管理组件
- 定义navList数组:根据底部导航条分类,集中存储路由必需信息对象
const navList = [
{
path: '/boss',
text: '牛人',
icon: 'boss',
title: '牛人列表',
component: Boss,
hide: user.type == 'genius'
},
{
path: '/genius',
text: 'Boss',
icon: 'job',
title: 'Boss列表',
component: Genius,
hide: user.type == 'boss'
},
{
path: '/msg',
text: '消息',
icon: 'msg',
title: '消息列表',
component: Msg
},
{
path: '/me',
text: '我',
icon: 'user',
title: '个人中心',
component: User
}
] 使用NavBar组件:判断当前pathname与路由对象中的path相等时,找到对应的路由对象,显示顶部导航信息title
import {NavBar} from 'antd-mobile' const {pathname} = this.props.location <NavBar mode='dard'>{navList.find(v => v.path==pathname).title}</NavBar>应用NavLinkBar底部导航组件:将路由信息数组navList传入组件
import NavLinkBar from '../navlink/navlink' <NavLinkBar data={navList}></NavLinkBar>注意:这里信息数组是必须要传的,因此需在navlink.js中添加组件属性类型校验为isRequired
import PropTypes from 'prop-types' static propTypes = {
data: PropTypes.array.isRequired
}
component目录下:创建navlink.js底部导航组件 ↑
- 用户信息,聊天列表,职位列表页面共享底部TabBar
- 对获取到的navList先进行过滤:通过.filter过滤掉依据当前user.type指定要hide的路由对象
- 过滤后的navList通过遍历:为每一个要显示的路由对象设置TabBar.Item
import React from 'react'
import {TabBar} from 'antd-mobile'
import PropTypes from 'prop-types'
import {withRouter} from 'react-router-dom' @withRouter
class NavLinkBar extends React.Component{
static propTypes = {
data: PropTypes.array.isRequired
}
render(){
const {pathname} = this.props.location
const navList = this.props.data.filter(v=>!v.hide)
// console.log(navList)
return (
<TabBar>
{navList.map(v=>(
<TabBar.Item
key={v.path}
title={v.text}
icon={{uri: require(`./img/${v.icon}.png`)}}
selectedIcon={{uri: require(`./img/${v.icon}-active.png`)}}
selected={pathname===v.path}
onPress={()=>{
this.props.history.push(v.path)
}}
>
</TabBar.Item>
))}
</TabBar>
)
}
} export default NavLinkBar
牛人列表
- Boss用户在/bose中看到的是牛人列表
- dashboard.js中:通过Switch组件将遍历navList得到且匹配到的第一个路由对象信息注入<Route/>,实现导航对应的不同页面内容
<div style={{marginTop: 45}}>
<Switch>
{navList.map(v => (
<Route key={v.path} path={v.path} component={v.component}></Route>
))}
</Switch>
</div> boss.js中:调用axios.get('/user/list?type=genius'),获得所有牛人用户的信息
componentDidMount(){
axios.get('/user/list?type=genius')
.then(res => {
if(res.data.code==0){
this.setState({data:res.data.data})
}
})
}
- 遍历所有牛人用户的信息对象,使用Card、Card.Header、Card.Body显示牛人信息
判断牛人有无头像:如没有则默认没有完善用户信息,不显示在牛人列表中
render(){
// console.log(this.state.data)
const Header = Card.Header
const Body = Card.Body
return (
<WingBlank>
<WhiteSpace />
{this.state.data.map(v=>(
v.avatar ? <Card key={v._id}>
<Header
title={v.user}
thumb={require(`../img/${v.avatar}.png`)}
extra={<span>{v.title}</span>}
>
</Header>
<Body>
{v.desc.split('\n').map(item=>(
<div key={item}>{item}</div>
))}
</Body>
</Card> : null
))}
</WingBlank>
)
}
user.js中:修改/user/list的get请求,获取req.query中的type参数,依据type查询User中的对应类型的数据
//用户信息列表
Router.get('/list', function(req, res){
const {type} = req.query
// User.remove({}, function(err, doc){})
User.find({type}, function(err, doc){
return res.json({code:0, data:doc})
})
})
Router.get的参数:用res.query获取
Router.post的参数:用res.body获取
使用redux管理牛人列表
- redux目录下:创建chatuser.redux.js用户聊天相关redux数据管理
import axios from 'axios'
//action type
const USER_LIST = 'USER_LIST' const initState = {
userlist:[]
} //reducer
export function chatuser(state=initState, action){
switch(action.type){
case USER_LIST:
return {...state, userlist:action.payload}
default:
return state
}
} //action creator
function userList(data){
return { type:USER_LIST, payload:data}
} //异步操作数据的方法
export function getUserList(type){
return dispatch=>{
axios.get('/user/list?type='+type)
.then(res=>{
if (res.data.code==0) {
//dispatch触发数据变化,执行action
dispatch(userList(res.data.data))
}
})
}
} reducer.js中:将chatuser合并进reducer,返回
import { chatuser } from './redux/chatuser.redux' export default combineReducers({user, chatuser})boss.js中:不再需要调用axios获取数据,而是使用connect连接组件和redux,并使用getUserList方法异步获取数据
import {connect} from 'react-redux'
import {getUserList} from '../../redux/chatuser.redux' @connect(
state => state.chatuser,
{getUserList}
) componentDidMount(){
this.props.getUserList('genius')
// axios.get('/user/list?type=genius')
// .then(res => {
// if(res.data.code==0){
// this.setState({data:res.data.data})
// }
// })
} //遍历userlist获得牛人信息对象
{this.props.userlist.map(v=>(
优化组件
- Boss列表组件和牛人列表组件的实现逻辑相同,会有大部分的复用代码,现将其抽离为一个基础组件
- UserCard.js中:通过Card相关组件显示获取到的用户信息
- 设置userlist为必需传的数组属性类型
- Boss列表有两个不同的选项,判断v.type若存在显示这两条信息
import React from 'react'
import PropTypes from 'prop-types'
import {Card, WhiteSpace, WingBlank} from 'antd-mobile' class UserCard extends React.Component{
static propTypes = {
userlist: PropTypes.array.isRequired
}
render(){
const Header = Card.Header
const Body = Card.Body
return (
<WingBlank>
<WhiteSpace />
{this.props.userlist.map(v=>(
v.avatar ? <Card key={v._id}>
<Header
title={v.user}
thumb={require(`../img/${v.avatar}.png`)}
extra={<span>{v.title}</span>}
>
</Header>
<Body>
{v.type=='boss' ? <div>公司:{v.company}</div> : null}
{v.desc.split('\n').map(d=>(
<div key={d}>{d}</div>
))}
{v.type=='boss' ? <div>薪资:{v.money}</div> : null}
</Body>
</Card> : null
))}
</WingBlank>
)
}
} export default UserCard
boss.js和genius.js中:删除重复代码,直接返回UserCard组件,传入redux中的userlist即可
return <UserCard userlist={this.props.userlist}></UserCard>
注:项目来自慕课网
【招聘App】—— React/Nodejs/MongoDB全栈项目:信息完善&用户列表的更多相关文章
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:登录注册
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:消息列表
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:socket.io&聊天实现
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:项目准备
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:个人中心&退出登录
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- 全栈项目|小书架|服务器端-NodeJS+Koa2实现首页图书列表接口
通过上篇文章 全栈项目|小书架|微信小程序-首页水平轮播实现 我们实现了前端(小程序)效果图的展示,这篇文章来介绍服务器端的实现. 首页书籍信息 先来回顾一下首页书籍都有哪些信息: 从下面的图片可以看 ...
- 全栈项目|小书架|服务器端-NodeJS+Koa2 实现书籍详情接口
通过上篇文章 全栈项目|小书架|微信小程序-首页水平轮播实现 我们实现了前端(小程序)效果图的展示,这篇文章来介绍服务器端的实现. 书籍详情分析 书籍详情页面如下: 从上图可以分析出详情页面大概有以下 ...
- 全栈项目|小书架|服务器开发-NodeJS 使用 JWT 实现登录认证
通过这篇 全栈项目|小书架|服务器开发-JWT 详解 文章我们对JWT有了深入的了解,那么接下来介绍JWT如何在项目中使用. 安装 $ npm install jsonwebtoken 生成 Toke ...
- Vue、Node全栈项目~面向小白的博客系统~
个人博客系统 前言 ❝ 代码质量问题轻点喷(去年才学的前端),有啥建议欢迎联系我,联系方式见最下方,感谢! 页面有啥bug也可以反馈给我,感谢! 这是一套包含前后端代码的个人博客系统,欢迎各位提出建议 ...
随机推荐
- Chrome扩展及应用开发
Chrome扩展及应用开发(电子书) http://www.ituring.com.cn/minibook/950 文档 官方 https://developer.chrome.com/extensi ...
- Storm中log4j日志打印不出来的解决办法
使用storm命令启动JAVA进程的时候,发现log4j日志打印不出来,咋办呢? 解决办法如下(亲测): 删除strom/lib目录下的log4j-over-slf4j-1.6.6.jar strom ...
- 【C++】各种成员变量
来自:黄邦勇帅 const 常量对象: 即把对象声明为常量,即 const hyong m,常量对象不能调用可能改变对象的值的函数,因此常量对象只能调用类中的 const 常量函数,因为不是 cons ...
- java.lang.NumberFormatException: multiple points 异常
平时使用SimpleDateFormat的时候都是在单线程的情况下使用的,今天在改写别人的代码,发现每个类中都会写大量的SimpleDateFormat实例.作为一个程序特有的洁癖开始对代码进行优化. ...
- python的算法:二分法查找(1)
1.什么是二分法查找: 1.从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束: 2.如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从 ...
- Qt笔记——QFile,QDataStream,QTextStream
QFile #ifndef WIDGET_H #define WIDGET_H #include <QWidget> namespace Ui { class Widget; } clas ...
- 上传文件提示413 Request Entity Too Large错误
打开nginx主配置文件nginx.conf 一般在/usr/local/nginx/conf/nginx.conf这个位置 找到http{}段并修改以下内容 client_max_body_size ...
- 错误 NETSDK1068: 框架依赖型应用程序主机需要一个至少 “netcoreapp2.1” 的目标框架
错误 NETSDK1068: 框架依赖型应用程序主机需要一个至少 “netcoreapp2.1” 的目标框架 我有一个ASP.NET Core 2网站应用程序,编译运行都没有问题,但是发布时却出了错, ...
- 部署web应用到虚拟主机的三种方式
方式一: 在 [tomcat]/conf/server.xml 文件中的<Engine>标签下的<Host>标签内部, 添加一个 <Context ...
- owasp zap 安全审计工具 的fuzzer使用
owasp zap 安全审计工具 的fuzzer可用场景如下: 一.SQL注入和XSS攻击等 1.选中请求中需要检查的字段值,右键-Fuzzy 2.选中file fuzzer功能(包括SQL注入,xs ...