zoukankan      html  css  js  c++  java
  • 【招聘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={{ 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
    1. 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}
      }
    2. 定义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

    1. 点击保存按钮时,调用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>  
    2. 判断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基础数据类型,包括数组、布尔、函数、数字、对象、字符串

    1. optionalArray: React.PropTypes.array,

    2. optionalBool: React.PropTypes.bool,

    3. optionalFunc: React.PropTypes.func,

    4. optionalNumber: React.PropTypes.number,

    5. optionalObject: React.PropTypes.object,

    6. optionalString: React.PropTypes.string

    • 如果不能为空isRequired

      avatar-selector.js中:指定组件属性类型为函数,且不能为空

      import PropTypes from 'prop-types'
      
      static propTypes = {
           selectAvatar: PropTypes.func.isRequired
      }
    • 指定数据类型成数组

      组件名称:PropTypes.arrayOf(PropTypes.number)  

    • 指定数据类型到对象

      组件名称:PropTypes.objectOf(PropTypes.number)  

      

    二、用户列表  

        应用骨架

    • index.js中:添加路由组件DashBoard
    1. 不设置path,则当路由匹配不到对应组件时,都会跳转到DashBoard组件中
    2. 使用DashBoard组件同时管理四个路由:/boss,/genius,/msg,/me
      import DashBoard from './component/dashboard/dashboard'
      
      <Route component={DashBoard}></Route>//一定要放在<Switch>内容的最下方  

        导航和跳转

    •  component目录下:创建dashboard管理组件
    1. 定义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
                  }
              ]
    2. 使用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>
    3. 应用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底部导航组件 ↑ 

    1. 用户信息,聊天列表,职位列表页面共享底部TabBar
    2. 对获取到的navList先进行过滤:通过.filter过滤掉依据当前user.type指定要hide的路由对象
    3. 过滤后的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})
                    }
                 })
      }  
    1. 遍历所有牛人用户的信息对象,使用Card、Card.Header、Card.Body显示牛人信息
    2. 判断牛人有无头像:如没有则默认没有完善用户信息,不显示在牛人列表中

      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('
      ').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})
          })
      })  
    1. Router.get的参数:用res.query获取

    2. 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相关组件显示获取到的用户信息 
    1. 设置userlist为必需传的数组属性类型   
    2. 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('
      ').map(d=>(
                                     <div key={d}>{d}</div>
                                 ))}
                                 {v.type=='boss' ? <div>薪资:{v.money}</div> : null}
                              </Body>
                          </Card> : null
                      ))}
                  </WingBlank>
               )
           }
      }
      
      export default UserCard
      
    • boss.jsgenius.js中:删除重复代码,直接返回UserCard组件,传入redux中的userlist即可

      return <UserCard userlist={this.props.userlist}></UserCard>

    注:项目来自慕课网  

  • 相关阅读:
    PostgreSQL中的partition-wise join
    Partition-wise join
    外观模式 门面模式 Facade 结构型 设计模式(十三)
    桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
    组合模式 合成模式 COMPOSITE 结构型 设计模式(十一)
    创建型设计模式对比总结 设计模式(八)
    原型模式 prototype 创建型 设计模式(七)
    单例模式 创建型 设计模式(六)
    建造者模式 生成器模式 创建型 设计模式(五)
    抽象工厂模式 创建型 设计模式(四)
  • 原文地址:https://www.cnblogs.com/ljq66/p/10274005.html
Copyright © 2011-2022 走看看