react 项目代码

H5 前端课程系列

———————————————————————————

1

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

React 全栈项目: 硅谷直聘

1 : 准备

1.1. 项目描述

1) 此项目为一个前后台分离招聘的 SPA, 包括前端应用和后端应用

2) 包括用户注册/登陆, 大神/老板列表, 实时聊天等模块

3) 前端: 使用 React 全家桶+ES6+Webpack 等技术

4) 后端: 使用 Node + express + mongodb + socketIO 等技术

5) 采用模块化、组件化、工程化的模式开发

1.2. 项目功能界面

大神主界面 大神消息列表 大神个人中心H5 前端课程系列

———————————————————————————

2

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

laoban 主界面 laoban 消息列表 laoban 个人中心

聊天界面 登陆界面 注册界面H5 前端课程系列

———————————————————————————

3

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

老板信息完善界面 大神信息完善界面H5 前端课程系列

———————————————————————————

4

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

1.3. 技术选型H5 前端课程系列

———————————————————————————

5

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

1.4. 前端路由H5 前端课程系列

———————————————————————————

6

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

1.5. API 接口

1.6. 你能从此项目中学到什么?

1.6.1. 流程及开发方法

1) 熟悉一个项目的开发流程

2) 学会模块化、组件化、工程化的开发模式

3) 掌握使用 create-react-app 脚手架初始化 react 项目开发

4) 学会使用 node+express+mongoose+mongodb 搭建后台开发

1.6.2. React 插件或第三方库

1) 学会使用 react-router-dom 开发单页应用

2) 学会使用 axios 与后端进行数据交互

3) 学会使用 redux+react-redux+redux-thunk 管理应用组件状态

4) 学会使用 antd-mobile 组件库构建界面

5) 学会使用 mongoose 操作 mongodb 数据库H5 前端课程系列

———————————————————————————

7

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

6) 学会使用 express 搭建后台路由

7) 学会使用 socket.io 实现实时通信

8) 学会使用 blueimp-md5 对密码进行 MD5 加密处理

9) 学会使用 js-cookies 操作浏览器端 cookie 数据

1.7. npm 常用命令

* npm init //初始化当前应用包, 生成 package.json

* npm install //根据 package.json 下载所有依赖包

* npm install packageName --save //下载某个运行时依赖包

* npm install packageName --save-dev //下载某个开发编译期依赖包

* npm install packageName -g //全局下载某个依赖包

* npm install package@version //下载指定版本的某个依赖包

* npm info packageName //查看某个包有远程仓库中的相关信息

* npm rm packageName --save //移除已下载的运行依赖包

* npm rm packageName –save-dev //移除已下载的开发依赖包

* npm list //查看安装的所有的包

* npm help //查看命令的详细帮助

* npm install -g cnpm --registry=https://registry.npm.taobao.org //安装淘宝镜像

* npm config set registry="https://registry.npm.taobao.org" //将淘宝镜像设置为 npm 的默认仓库

* npm run xxx //执行 package.json scripts 中配置的命令

* npm root -g //查看全局下载目录

1.8. git 常用基本命令

* git config --global user.name "username" //配置用户名

* git config --global user.password "[email protected]" //配置邮箱

* git init //初始化生成一个本地仓库

* git clone url //将远程仓库克隆下载到本地

* git add * //添加到暂存区

* git commit –m “message” //提交到本地仓库

* git remote add origin url //关联到远程仓库

* git push origin master //push 到远程

* git pull origin master //从远程 pull 更新H5 前端课程系列

———————————————————————————

8

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2 : 应用开发详解

2.1. 开启项目开发

2.1.1. 使用 create-react-app(脚手架)搭建项目

1) create-react-app react 官方提供的用于搭建基于 react+webpack+es6 项目的脚手架

2) 操作:

npm install -g create-react-app : 全局下载工具

create-react-app gzhipin-client :下载模板项目

cd gzhipin

npm start

访问: localhost:3000

2.1.2. 编码测试与打包发布项目

1) 编码测试

npm start

访问: http://localhost:3000

编码, 自动编译打包刷新(live-reload), 查看效果

2) 打包发布

npm run build

npm install -g serve

serve build

访问: http://localhost:5000

2.2. 功能需求分析

演示项目功能, 对功能模块进行分析说明H5 前端课程系列

———————————————————————————

9

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.3. 项目(前端)源码目录设计

2.4. 引入 antd-mobile

2.4.1. 下载组件库包

npm install antd-mobile --save

2.4.2. 页面处理: index.html

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,

minimum-scale=1, user-scalable=no" />

<script

src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js">scrip

t>

<script>

if ('addEventListener' in document) {

document.addEventListener('DOMContentLoaded', function() {

FastClick.attach(document.body);

}, false);

}

if(!window.Promise) {

document.writeln('<< span="">H5 前端课程系列

———————————————————————————

10

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"

'+'>'+'<'+'/'+'script>');

}

script>

2.4.3. 实现组件的按需打包

1) 下载依赖模块

npm install --save-dev babel-plugin-import react-app-rewired

2) 定义加载配置的 js 模块: config-overrides.js

const {injectBabelPlugin} = require('react-app-rewired');

module.exports = function override(config, env) {

config = injectBabelPlugin(['import', {libraryName: 'antd-mobile', style: 'css'}],

config);

return config;

}

修改配置: package.json

"scripts": {

"start": "react-app-rewired start",

"build": "react-app-rewired build",

"test": "react-app-rewired test --env=jsdom",

"eject": "react-scripts eject"

}

2.4.4. 在应用中使用 antd 组件

import React from 'react'

import ReactDOM from 'react-dom'

import {Button} from 'antd-mobile'

ReactDOM.render(

<Button type='primary'>学习Button>,

document.getElementById('root')

)H5 前端课程系列

———————————————————————————

11

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.4.5. 应用中使用的组件

2.4.6. 自定义主题

1) 目标:

将主体的背景颜色从 blue 变为 green

2) 下载依赖模块

npm install --save-dev [email protected] less-loader

3) 配置: config-overrides.js

const {injectBabelPlugin, getLoader} = require('react-app-rewired');

const fileLoaderMatcher = function (rule) {

return rule.loader && rule.loader.indexOf(`file-loader`) != -1;H5 前端课程系列

———————————————————————————

12

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

module.exports = function override(config, env) {

// babel-plugin-import

config = injectBabelPlugin(['import', {

libraryName: 'antd-mobile',

//style: 'css',

style: true, // use less for customized theme

}], config);

// customize theme

config.module.rules[1].oneOf.unshift(

{

test: /\.less$/,

use: [

require.resolve('style-loader'),

require.resolve('css-loader'),

{

loader: require.resolve('postcss-loader'),

options: {

// Necessary for external CSS imports to work

// https://github.com/facebookincubator/create-react-app/issues/2677

ident: 'postcss',

plugins: () => [

require('postcss-flexbugs-fixes'),

autoprefixer({

browsers: [

'>1%',

'last 4 versions',

'Firefox ESR',

'not ie < 9', // React doesn't support IE8 anyway

],

flexbox: 'no-2009',

}),

],

},

},

{

loader: require.resolve('less-loader'),

options: {

// theme vars, also can use theme.js instead of this.

modifyVars: {H5 前端课程系列

———————————————————————————

13

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

"@brand-primary": "#1cae82", // 正常

"@brand-primary-tap": "#1DA57A", // 按下

},

},

},

]

}

);

// css-modules

config.module.rules[1].oneOf.unshift(

{

test: /\.css$/,

exclude: /node_modules|antd-mobile\.css/,

use: [

require.resolve('style-loader'),

{

loader: require.resolve('css-loader'),

options: {

modules: true,

importLoaders: 1,

localIdentName: '[local]___[hash:base64:5]'

},

},

{

loader: require.resolve('postcss-loader'),

options: {

// Necessary for external CSS imports to work

// https://github.com/facebookincubator/create-react-app/issues/2677

ident: 'postcss',

plugins: () => [

require('postcss-flexbugs-fixes'),

autoprefixer({

browsers: [

'>1%',

'last 4 versions',

'Firefox ESR',

'not ie < 9', // React doesn't support IE8 anyway

],

flexbox: 'no-2009',

}),

],H5 前端课程系列

———————————————————————————

14

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

},

},

]

}

);

// file-loader exclude

let l = getLoader(config.module.rules, fileLoaderMatcher);

l.exclude.push(/\.less$/);

return config;

};

2.5. 引入路由

2.5.1. 下载路由包: react-router-dom

npm install --save react-router-dom

2.5.2. 路由组件: containers/register/register.jsx

/*

用户注册的路由组件

*/

import React, {Component} from 'react'

export default class Register extends Component {

render() {

return (

<div>Registerdiv>

)

}

}H5 前端课程系列

———————————————————————————

15

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.5.3. 路由组件: containers/login/login.jsx

/*

用户登陆的路由组件

*/

import React, {Component} from 'react'

export default class Login extends Component {

render () {

return (

<div>logindiv>

)

}

}

2.5.4. 路由组件: containers/main/main.jsx

/*

应用主界面路由组件

*/

import React, {Component} from 'react'

export default class Main extends Component {

render() {

return (

<div>Maindiv>

)

}

}

2.5.5. 映射路由: index.js

/*

入口 JS

*/

import React from 'react'

import ReactDOM from 'react-dom'H5 前端课程系列

———————————————————————————

16

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import {HashRouter, Switch, Route} from 'react-router-dom'

import Login from './containers/login/login'

import Register from './containers/register/register'

import Main from './containers/main/main'

ReactDOM.render((

<HashRouter>

<Switch>

<Route path='/login' component={Login}/>

<Route path='/register' component={Register}/>

<Route component={Main}/>

Switch>

HashRouter>

), document.getElementById('root'))

2.6. 引入 redux

2.6.1. 下载相关依赖包

npm install --save [email protected] react-redux redux-thunk

npm install --save-dev redux-devtools-extension

注意: redux 不能下载最新版本

2.6.2. reducers: redux/reducers.js

/*

包含多个用于生成新的 state reducer 函数的模块

*/

import {combineReducers} from 'redux'

function xxx(state = 0, action) {

return stateH5 前端课程系列

———————————————————————————

17

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

function yyy(state = 0, action) {

return state

}

// 返回合并后的 reducer 函数

export default combineReducers({

xxx,

yyy

})

2.6.3. store: redux/store.js

/*

redux 最核心的 store 对象模块

*/

import {createStore, applyMiddleware} from 'redux'

import thunk from 'redux-thunk'

import {composeWithDevTools} from 'redux-devtools-extension'

import reducers from './reducers'

export default createStore(reducers, composeWithDevTools(applyMiddleware(thunk)))

2.6.4. 入口 JS: index.js

/*

入口 JS

*/

import React from 'react'

import ReactDOM from 'react-dom'

import {Provider} from 'react-redux'

import {HashRouter, Switch, Route} from 'react-router-dom'

import store from './redux/store'

import Login from './containers/login/login'H5 前端课程系列

———————————————————————————

18

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import Register from './containers/register/register'

import Main from './containers/main/main'

ReactDOM.render((

<Provider store={store}>

<HashRouter>

<Switch>

<Route path='/login' component={Login}/>

<Route path='/register' component={Register}/>

<Route component={Main}/>

Switch>

HashRouter>

Provider>

), document.getElementById('root'))

2.7. 注册/登陆界面

2.7.1. Logo 组件

1) 引入 logo 图片H5 前端课程系列

———————————————————————————

19

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

logo.png

2) components/logo/logo.jsx

import React from 'react'

import logo from './logo.png'

import './logo.less'

/*

简单的显示 logo 的组件

*/

export default class Logo extends Component {

render () {

return (

<div className="logo-container">

<img src={logo} alt="logo" className='logo-img'/>

div>

)

}

}

3) components/logo/logo.less

.logo-container {

text-align: center;

margin-top: 10px;

margin-bottom: 10px;

.logo-img {

width: 240px;

height: 240px;

}

}

2.7.2. 注册组件: containers/register/register.jsx

/*

用户注册的路由组件

*/H5 前端课程系列

———————————————————————————

20

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import React, {Component} from 'react'

import {

NavBar,

WingBlank,

List,

InputItem,

WhiteSpace,

Radio,

Button

} from 'antd-mobile'

import Logo from '../../components/logo/logo'

export default class Register extends Component {

state = {

username: '',

password: '',

password2: '',

type: 'dashen'

}

// 处理输入框/单选框变化, 收集数据到 state

handleChange = (name, value) => {

this.setState({[name]: value})

}

// 跳转到 login 路由

toLogin = () => {

this.props.history.replace('/login')

}

// 注册

register = () => {

console.log(JSON.stringify(this.state))

}

render() {

const {type} = this.state

return (

<div>

<NavBar>硅谷直聘NavBar>H5 前端课程系列

———————————————————————————

21

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

<Logo/>

<WingBlank>

<List>

<InputItem

placeholder='输入用户名'

onChange={val => this.handleChange('username', val)}

>

用户名:

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入密码'

onChange={val => this.handleChange('password', val)}

>

   :

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入确认密码'

onChange={val => this.handleChange('password2', val)}

>

确认密码:

InputItem>

<WhiteSpace/>

<List.Item>

<span style={{marginRight: 30}}>用户类型:span>

<Radio checked={this.state.type==='dashen'}

onClick={() => {this.handleChange('type', 'dashen')}}>大神Radio>

      

<Radio checked={this.state.type==='laoban'}

onClick={() => {this.handleChange('type', 'laoban')}}>老板Radio>

List.Item>

<WhiteSpace/>

<Button type='primary' onClick={this.register}>   

Button>

<WhiteSpace/>

<Button onClick={this.toLogin}>已经有账号Button>

List>

WingBlank>

div>H5 前端课程系列

———————————————————————————

22

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

)

}

}

2.7.3. 登陆组件: containers/login/login.jsx

/*

用户登陆的路由组件

*/

import React, {Component} from 'react'

import {

NavBar,

WingBlank,

List,

InputItem,

WhiteSpace,

Button

} from 'antd-mobile'

import Logo from '../../components/logo/logo'

export default class Login extends Component {

state = {

username: '',

password: '',

}

// 处理输入框/单选框变化, 收集数据到 state

handleChange = (name, value) => {

this.setState({[name]: value})

}

// 跳转到注册路由

toRegister = () => {

this.props.history.replace('/register')

}

// 注册H5 前端课程系列

———————————————————————————

23

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

login = () => {

console.log(this.state)

}

render() {

return (

<div>

<NavBar>硅谷直聘NavBar>

<Logo/>

<WingBlank>

<List>

<InputItem

placeholder='输入用户名'

onChange={val => this.handleChange('username', val)}

>

用户名:

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入密码'

onChange={val => this.handleChange('password', val)}

>

   :

InputItem>

<WhiteSpace/>

<Button type='primary' onClick={this.login}>   

Button>

<WhiteSpace/>

<Button onClick={this.toRegister}>还没有账号Button>

List>

WingBlank>

div>

)

}

}H5 前端课程系列

———————————————————————————

24

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.8. 搭建后台应用

2.8.1. 创建 node+express 应用

2.8.2. 后台应用结构H5 前端课程系列

———————————————————————————

25

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.8.3. 启动应用并访问

1) 执行命令: npm start

2) 访问: http://localhost:3000

2.8.4. 后台简单编码并测试

1) 需求:

a. 后台应用运行端口指定为 4000

b. 提供一个用户注册的接口

a) path : /register

b) 请求方式为: POST

c) 接收 username password 参数

d) admin 是已注册用户

e) 注册成功返回: {code: 0, data: {_id: 'abc', username: ‘xxx’, password:’123’}

f) 注册失败返回: {code: 1, msg: '此用户已存在'}

2) 修改端口号: bin/www

var port = normalizePort(process.env.PORT || '4000');

3) 注册新路由: routes/index.js

router.post('/register', function (req, res, next) {

const {username, password} = req.body

console.log('register', username, password)

if (username === 'admin') {

res.send({code: 1, msg: '此用户已存在'})

} else {

res.send({code: 0, data: {_id: 'abc', username, password}})

}

})

4) 重启应用

npm startH5 前端课程系列

———————————————————————————

26

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

5) 使用 postman 测试接口

http://blog.csdn.net/ye1992/article/details/49998511: 不同的 post 提交方式

2.8.5. 后台应用自动重运行

1) 问题: 每次修改后台应用代码, 需要重新运行命令修改才生效

2) 解决: 使用 nodemon

3) 下载: npm install --save-dev nodemon

4) 配置: "start": "nodemon ./bin/www"

5) 测试: 修改后台任何代码, 会自动重新运行最新的代码 (按下 ctrl + S)

2.9. 使用 mongoose 操作数据库

2.9.1. 下载依赖包

npm install --save mongoose blueimp-md5H5 前端课程系列

———————————————————————————

27

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.9.2. db/db_test.js

/*

使用 mongoose 操作 mongodb 的测试文件

1. 连接数据库

1.1. 引入 mongoose

1.2. 连接指定数据库(URL 只有数据库是变化的)

1.3. 获取连接对象

1.4. 绑定连接完成的监听(用来提示连接成功)

2. 得到对应特定集合的 Model

2.1. 字义 Schema(描述文档结构)

2.2. 定义 Model(与集合对应, 可以操作集合)

3. 通过 Model 或其实例对集合数据进行 CRUD 操作

3.1. 通过 Model 实例的 save()添加数据

3.2. 通过 Model find()/findOne()查询多个或一个数据

3.3. 通过 Model findByIdAndUpdate()更新某个数据

3.4. 通过 Model remove()删除匹配的数据

*/

const md5 = require('blueimp-md5')

// 1. 连接数据库

// 1.1. 引入 mongoose

const mongoose = require('mongoose')

// 1.2. 连接指定数据库(URL 只有数据库是变化的)

mongoose.connect('mongodb://localhost:27017/gzhipin_test2')

// 1.3. 获取连接对象

const conn = mongoose.connection

// 1.4. 绑定连接完成的监听(用来提示连接成功)

conn.on('connected', function () {

console.log('数据库连接成功')

})

// 2. 得到对应特定集合的 Model

// 2.1. 字义 Schema(描述文档结构)

const userSchema = mongoose.Schema({

username: {type: String, required: true}, // 用户名

password: {type: String, required: true}, // 密码

type: {type: String, required: true}, // 用户类型: dashen/laoban

})

// 2.2. 定义 Model(与集合对应, 可以操作集合)

const UserModel = mongoose.model('user', userSchema) // 集合名: usersH5 前端课程系列

———————————————————————————

28

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// CRUD

// 3.1. 通过 Model 实例的 save()添加数据

function testSave() {

// user 数据对象

const user = {

username: 'xfzhang',

password: md5('1234'),

type: 'dashen',

}

const userModel = new UserModel(user)

// 保存到数据库

userModel.save(function (err, user) {

console.log('save', err, user)

})

}

// testSave()

// 3.2. 通过 Model find()/findOne()查询多个或一个数据

function testFind() {

// 查找多个

UserModel.find(function (err, users) { // 如果有匹配返回的是一个[user, user..], 如果

没有一个匹配的返回[]

console.log('find() ', err, users)

})

// 查找一个

UserModel.findOne({_id: '5ae1d0ab28bd750668b3402c'}, function (err, user) { // 如果

有匹配返回的是一个 user, 如果没有一个匹配的返回 null

console.log('findOne() ', err, user)

})

}

// testFind()

// 3.3. 通过 Model findByIdAndUpdate()更新某个数据

function testUpdate() {

UserModel.findByIdAndUpdate({_id: '5ae1241cf2dd541a8c59a981'}, {username: 'yyy'},

function (err, user) {

console.log('findByIdAndUpdate()', err, user)H5 前端课程系列

———————————————————————————

29

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

})

}

// testUpdate()

// 3.4. 通过 Model remove()删除匹配的数据

function testDelete() {

UserModel.remove({_id: '5ae1241cf2dd541a8c59a981'}, function (err, result) {

console.log('remove()', err, result)

})

}

// testDelete()

2.9.3. 安装 webstorm mongodb 插件: Mongo PluginH5 前端课程系列

———————————————————————————

30

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.10. 注册/登陆后台处理

2.10.1. 数据库数据操作模块: db/models.js

/*

包含 n 个能操作 mongodb 数据库集合的 model 的模块

1. 连接数据库

1.1. 引入 mongoose

1.2. 连接指定数据库(URL 只有数据库是变化的)

1.3. 获取连接对象

1.4. 绑定连接完成的监听(用来提示连接成功)

2. 定义出对应特定集合的 Model 并向外暴露

2.1. 字义 Schema(描述文档结构)

2.2. 定义 Model(与集合对应, 可以操作集合)

2.3. 向外暴露 Model

*/H5 前端课程系列

———————————————————————————

31

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

/*1. 连接数据库*/

// 1.1. 引入 mongoose

const mongoose = require('mongoose')

// 1.2. 连接指定数据库(URL 只有数据库是变化的)

mongoose.connect('mongodb://localhost:27017/bossz')

// 1.3. 获取连接对象

const conn = mongoose.connection

// 1.4. 绑定连接完成的监听(用来提示连接成功)

conn.on('connected', function () {

console.log('数据库连接成功!')

})

/*2. 定义出对应特定集合的 Model 并向外暴露*/

// 2.1. 字义 Schema(描述文档结构)

const userSchema = mongoose.Schema({

username: {type: String, required: true}, // 用户名

password: {type: String, required: true}, // 密码

type: {type: String, required: true}, // 用户类型: dashen/laoban

header: {type: String}, // 头像名称

post: {type: String}, // 职位

info: {type: String}, // 个人或职位简介

company: {type: String}, // 公司名称

salary: {type: String} // 工资

})

// 2.2. 定义 Model(与集合对应, 可以操作集合)

const UserModel = mongoose.model('user', userSchema)

// 2.3. 向外暴露 Model

exports.UserModel = UserModel

2.10.2. 路由器模块: routes/index.js

// 引入 md5 加密函数库

const md5 = require('blueimp-md5')

// 引入 UserModel

const UserModel = require('../db/models').UserModel

const filter = {password: 0} // 查询时过滤出指定的属性

// 注册路由

router.post('/register', function (req, res) {

// 1. 获取请求参数数据(username, password, type)H5 前端课程系列

———————————————————————————

32

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

const {username, password, type} = req.body

// 2. 处理数据

// 3. 返回响应数据

// 2.1. 根据 username 查询数据库, 看是否已存在 user

UserModel.findOne({username}, function (err, user) {

// 3.1. 如果存在, 返回一个提示响应数据: 此用户已存在

if(user) {

res.send({code: 1, msg: '此用户已存在'}) // code 是数据是否是正常数据的标识

} else {

// 2.2. 如果不存在, 将提交的 user 保存到数据库

new UserModel({username, password: md5(password), type}).save(function (err,

user) {

// 生成一个 cookie(userid: user._id), 并交给浏览器保存

res.cookie('userid', user._id, {maxAge: 1000*60*60*24*7}) // 持久化 cookie,

览器会保存在本地文件

// 3.2. 保存成功, 返回成功的响应数据: user

res.send({code: 0, data: {_id: user._id, username, type}}) // 返回的数据中不要

携带 pwd

})

}

})

})

// 登陆路由

router.post('/login', function (req, res) {

// 1. 获取请求参数数据(username, password)

const {username, password} = req.body

// 2. 处理数据: 根据 username password 去数据库查询得到 user

UserModel.findOne({username, password: md5(password)}, filter, function (err, user)

{

// 3. 返回响应数据

// 3.1. 如果 user 没有值, 返回一个错误的提示: 用户名或密码错误

if(!user) {

res.send({code: 1, msg: '用户名或密码错误'})

} else {

// 生成一个 cookie(userid: user._id), 并交给浏览器保存

res.cookie('userid', user._id, {maxAge: 1000*60*60*24*7})

// 3.2. 如果 user 有值, 返回 user

res.send({code: 0, data: user}) // user 中没有 pwd

}

})

})H5 前端课程系列

———————————————————————————

33

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.10.3. 使用 postman 测试接口

2.11. 注册/登陆前台处理

2.11.1. 下载相关依赖包

npm install --save axios

2.11.2. 封装 ajax 请求代码

1) api/ajax.js

/*

使用 axios 封装的 ajax 请求函数

函数返回的是 promise 对象

*/

import axios from 'axios'

export default function ajax(url = '', data = {}, type = 'GET') {

if (type === 'GET') {

// 准备 url query 参数数据

let dataStr = '' //数据拼接字符串

Object.keys(data).forEach(key => {

dataStr += key + '=' + data[key] + '&'

})

if (dataStr !== '') {

dataStr = dataStr.substring(0, dataStr.lastIndexOf('&'))

url = url + '?' + dataStr

}

// 发送 get 请求

return axios.get(url)

} else {

// 发送 post 请求

return axios.post(url, data) // data: 包含请求体数据的对象

}H5 前端课程系列

———————————————————————————

34

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

2) api/index.js

/*

包含 n 个接口请求函数的模块

每个函数返回的都是 promise 对象

*/

import ajax from './ajax'

// 请求注册

export const reqRegister = (user) => ajax('/register', user, 'POST')

// 请求登陆

export const reqLogin = (user) => ajax('/login', user, 'POST')

3) 配置 ajax 请求的代理: package.json

"proxy": "http://localhost:4000"

2.11.3. 使用 redux 管理用户信息

1) redux/action-types.js

/*

包含所有 action type 常量名称的模块

*/

// 验证成功

export const AUTH_SUCCESS = 'AUTH_SUCCESS'

// 请求出错

export const ERROR_MSG = 'ERROR_MSG'

2) redux/actions.js

/*

包含所有 action creator 函数的模块

*/

import {H5 前端课程系列

———————————————————————————

35

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

AUTH_SUCCESS,

ERROR_MSG

} from './action-types'

import {

reqRegister,

reqLogin

} from '../api'

// 同步错误消息

const errorMsg = (msg) => ({type:ERROR_MSG, data: msg})

// 同步成功响应

const authSuccess = (user) => ({type: AUTH_SUCCESS, data: user})

/*

异步注册

*/

export function register({username, password, password2, type}) {

// 进行前台表单验证, 如果不合法返回一个同步 action 对象, 显示提示信息

if (!username || !password || !type) {

return errorMsg('用户名密码必须输入')

}

if (password !== password2) {

return errorMsg('密码和确认密码不同')

}

return async dispatch => {

// 异步 ajax 请求, 得到响应

const response = await reqRegister({username, password, type})

// 得到响应体数据

const result = response.data

// 如果是正确的

if (result.code === 0) {

// 分发成功的 action

dispatch(authSuccess(result.data))

} else {

// 分发提示错误的 action

dispatch(errorMsg(result.msg))

}

}

}

/*H5 前端课程系列

———————————————————————————

36

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

异步登陆

*/

export const login = ({username, password}) => {

if (!username || !password) {

return errorMsg('用户密码必须输入')

}

return async dispatch => {

const response = await reqLogin({username, password})

const result = response.data

if (result.code === 0) {

dispatch(authSuccess(result.data))

} else {

dispatch(errorMsg(result.msg))

}

}

}

3) redux/reducers.js

/*

包含 n 个根据老的 state action 返回新的 state 的函数的模块

*/

import {combineReducers} from 'redux'

import {

AUTH_SUCCESS,

ERROR_MSG

} from './action-types'

const initUser = {

username: '', // 用户名

type: '', // 类型

msg: '', // 错误提示信息

redirectTo: '' // 需要自动跳转的路由 path

}

function user(state = initUser, action) {

switch (action.type) {

case AUTH_SUCCESS: // 认证成功

return {...action.data, redirectTo: '/'}

case ERROR_MSG: // 错误信息提示

return {...state, msg: action.data}H5 前端课程系列

———————————————————————————

37

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

default:

return state

}

}

// 返回合并的 reducer

export default combineReducers({

user

})

2.11.4. 注册组件: register.jsx

/*

用户注册的路由组件

*/

import React, {Component} from 'react'

import {

NavBar,

WingBlank,

List,

InputItem,

WhiteSpace,

Radio,

Button

} from 'antd-mobile'

import {connect} from 'react-redux'

import {Redirect} from 'react-router-dom'

import Logo from '../../components/logo/logo'

import {register} from '../../redux/actions'

class Register extends Component {

state = {

username: '',

password: '',

password2: '',

type: 'dashen'

}H5 前端课程系列

———————————————————————————

38

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 处理输入框/单选框变化, 收集数据到 state

handleChange = (name, value) => {

this.setState({[name]: value})

}

// 跳转到 login 路由

toLogin = () => {

this.props.history.replace('/login')

}

// 注册

register = () => {

this.props.register(this.state)

}

render() {

const {type} = this.state

const {redirectTo, msg} = this.props

if (redirectTo) {

return <Redirect to={redirectTo}/>

}

return (

<div>

<NavBar>硅谷直聘NavBar>

<Logo/>

<WingBlank>

{msg ? <p className='error-msg'>{msg}p> : null}

<List>

<InputItem

placeholder='输入用户名'

onChange={val => this.handleChange('username', val)}

>

用户名:

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入密码'

onChange={val => this.handleChange('password', val)}

>H5 前端课程系列

———————————————————————————

39

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

   :

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入确认密码'

onChange={val => this.handleChange('password2', val)}

>

确认密码:

InputItem>

<WhiteSpace/>

<List.Item>

<span style={{marginRight: 30}}>用户类型:span>

<Radio checked={this.state.type==='dashen'}

onClick={() => {this.handleChange('type', 'dashen')}}>大神Radio>

      

<Radio checked={this.state.type==='laoban'}

onClick={() => {this.handleChange('type', 'laoban')}}>老板Radio>

List.Item>

<WhiteSpace/>

<Button type='primary' onClick={this.register}>   

Button>

<WhiteSpace/>

<Button onClick={this.toLogin}>已经有账号Button>

List>

WingBlank>

div>

)

}

}

export default connect(

state => state.user,

{register}

)(Register)

2.11.5. 登陆组件: login.jsx

/*

用户登陆的路由组件H5 前端课程系列

———————————————————————————

40

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

*/

import React, {Component} from 'react'

import {

NavBar,

WingBlank,

List,

InputItem,

WhiteSpace,

Button

} from 'antd-mobile'

import {connect} from 'react-redux'

import {Redirect} from 'react-router-dom'

import Logo from '../../components/logo/logo'

import {login} from '../../redux/actions'

class Login extends Component {

state = {

username: '',

password: '',

}

// 处理输入框/单选框变化, 收集数据到 state

handleChange = (name, value) => {

this.setState({[name]: value})

}

// 跳转到注册路由

toRegister = () => {

this.props.history.replace('/register')

}

// 注册

login = () => {

this.props.login(this.state)

}

render() {

const {redirectTo, msg} = this.props

if (redirectTo) {

return <Redirect to={redirectTo}/>H5 前端课程系列

———————————————————————————

41

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

return (

<div>

<NavBar>硅谷直聘NavBar>

<Logo/>

<WingBlank>

{msg ? <p className='error-msg'>{msg}p> : null}

<List>

<InputItem

placeholder='输入用户名'

onChange={val => this.handleChange('username', val)}

>

用户名:

InputItem>

<WhiteSpace/>

<InputItem

type='password'

placeholder='输入密码'

onChange={val => this.handleChange('password', val)}

>

   :

InputItem>

<WhiteSpace/>

<Button type='primary' onClick={this.login}>   

Button>

<WhiteSpace/>

<Button onClick={this.toRegister}>还没有账号Button>

List>

WingBlank>

div>

)

}

}

export default connect(

state => state.user,

{login}

)(Login)H5 前端课程系列

———————————————————————————

42

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.11.6. 样式: assets/css/index.less

.error-msg {

color: red;

text-align: center;

font-size: 18px;

}

需要在 index.js 中引入

2.12. 实现 user 信息完善功能

2.12.1. 下载相关依赖包

npm install --save js-cookie

2.12.2. 注册/登陆成功的路由跳转

1) 工具模块: utils/index.js

/*

包含 n 个工具函数的模块

*/

/*

注册 laoban--> /laobaninfo

注册大神--> /dasheninfo

登陆 laoban --> /laobaninfo 或者 /laoban

登陆大神 --> /dasheninfo 或者 /dashen

*/

export function getRedirectPath(type, header) {

let path = ''

// 根据 type 得到 path

path += type==='laoban' ? '/laoban' : '/dashen'

// 如果没有头像添加 info

if(!header) {

path += 'info'H5 前端课程系列

———————————————————————————

43

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

return path

}

2) reducers 中使用工具: redux/reducers.js

import {getRedirectPath} from '../utils'

case AUTH_SUCCESS: // 认证成功

const redirectTo = getRedirectPath(action.data.type, action.data.header)

return {...action.data, redirectTo}

2.12.3. 后台路由: routes/index.js

// 更新用户路由

router.post('/update', function (req, res) {

// 得到请求 cookie userid

const userid = req.cookies.userid

if(!userid) {// 如果没有, 说明没有登陆, 直接返回提示

return res.send({code: 1, msg: '请先登陆')};

}

// 更新数据库中对应的数据

UserModel.findByIdAndUpdate({_id: userid}, req.body, function (err, user) {// user

是数据库中原来的数据

const {_id, username, type} = user

// node ...不可用

// const data = {...req.body, _id, username, type}

// 合并用户信息

const data = Object.assign(req.body, {_id, username, type})

// assign(obj1, obj2, obj3,...) // 将多个指定的对象进行合并, 返回一个合并后的对象

res.send({code: 0, data})

})

})H5 前端课程系列

———————————————————————————

44

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.12.4. 更新的 ajax 请求: api/index.js

// 更新用户信息

export const reqUpdateUser = (user) => ajax('/update', user, 'POST')

2.12.5. redux 更新状态:

1) redux/action-types.js

// 接收用户

export const RECEIVE_USER = 'RECEIVE_USER'

// 重置用户

export const RESET_USER = 'RESET_USER'

2) redux/actions.js

import {

RECEIVE_USER,

RESET_USER

} from './action-types'

import {

reqUpdateUser

} from '../api'

// 同步接收用户

const receiveUser = (user) => ({type: RECEIVE_USER, data: user})

// 同步重置用户

export const resetUser = (msg) => ({type: RESET_USER, data: msg})

/*

异步更新用户

*/

export const updateUser = (user) => {

return async dispatch => {

// 发送异步 ajax 请求

const response = await reqUpdateUser(user)

const result = response.data

if (result.code === 0) { // 更新成功

dispatch(receiveUser(result.data))

} else { // 失败H5 前端课程系列

———————————————————————————

45

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

dispatch(resetUser(result.msg))

}

}

}

3) redux/reducers.js

import {

RECEIVE_USER,

RESET_USER

} from './action-types'

function user(state = initUser, action) {

switch (action.type) {

case RECEIVE_USER: // 接收用户

return action.data

case RESET_USER: // 重置用户

return {...initUser, msg: action.data}

}

}

2.12.6. 用户头像选择组件

1) 添加多个头像图片文件: assets/imgsH5 前端课程系列

———————————————————————————

46

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2) 组件: components/header-selector/header-selector.jsx

/*

选择头像的组件

*/

import React, {Component} from 'react'

import {List, Grid} from 'antd-mobile'

import PropTypes from 'prop-types'

export default class HeaderSelector extends Component {

static propTypes = {

setHeader: PropTypes.func.isRequired

}

state = {

icon: null

}

constructor(props) {H5 前端课程系列

———————————————————————————

47

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

super(props)

this.headerList = []

for (var i = 0; i < 20; i++) {

const text = `头像${i+1}`

this.headerList.push({text, icon: require(`../../assets/imgs/${text}.png`)})

}

}

selectHeader = ({icon, text}) => {

// 更新当前组件的状态

this.setState({icon})

// 更新父组件的状态

this.props.setHeader(text)

}

render () {

// 计算头部显示

const {icon} = this.state

const gridHeader = icon ? <p>已选择头像: <img src={icon} alt="header"/>p> : '

选择头像'

return (

<List renderHeader={() => gridHeader}>

<Grid data={this.headerList}

columnNum={5}

onClick={this.selectHeader}/>

List>

)

}

}

2.12.7. 老板信息完善组件: containers/laoban-info/laoban-info.jsx

/*

laoban 信息完善路由组件

*/

import React, {Component} from 'react'

import {NavBar, InputItem, TextareaItem, Button} from 'antd-mobile'

import {connect} from 'react-redux'H5 前端课程系列

———————————————————————————

48

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import {Redirect} from 'react-router-dom'

import HeaderSelector from '../../components/header-selector/header-selector'

import {updateUser} from '../../redux/actions'

class LaobanInfo extends Component {

state = {

header: '', // 头像名称

info: '', // 职位简介

post: '', // 职位名称

company: '', // 公司名称

salary: '' // 工资

}

handleChange = (name, val) => {

this.setState({[name]: val})

}

// 设置更新 header

setHeader = (header) => {

this.setState({header})

}

render() {

const {user} = this.props

// 如果用户信息已完善, 自动跳转到 laoban 主界面

if(user.header) {

return <Redirect to='/laoban'/>

}

return (

<div>

<NavBar>老板信息完善NavBar>

<HeaderSelector setHeader={this.setHeader}/>

<InputItem onChange={val => this.handleChange('post', val)}>招聘职

:InputItem>

<InputItem onChange={val => this.handleChange('company', val)}>公司名

:InputItem>

<InputItem onChange={val => this.handleChange('salary', val)}>职位薪

:InputItem>H5 前端课程系列

———————————————————————————

49

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

<TextareaItem title="职位要求:"

rows={3}

onChange={val => this.handleChange('info', val)}/>

<Button type='primary' onClick={() => this.props.updateUser(this.state)}>保存

Button>

div>

)

}

}

export default connect(

state => ({user: state.user}),

{updateUser}

)(LaobanInfo)

2.12.8. 大神信息完善组件: containers/dashen-info/dashen-info.jsx

/*

大神信息完善路由组件

*/

import React, {Component} from 'react'

import {NavBar, InputItem, TextareaItem, Button} from 'antd-mobile'

import {connect} from 'react-redux'

import {Redirect} from 'react-router-dom'

import HeaderSelector from '../../components/header-selector/header-selector'

import {updateUser} from '../../redux/actions'

class DashenInfo extends Component {

state = {

header: '', // 头像名称

info: '', // 个人简介

post: '', // 求职岗位

}

handleChange = (name, val) => {

this.setState({[name]: val})

}H5 前端课程系列

———————————————————————————

50

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 设置更新 header

setHeader = (header) => {

this.setState({header})

}

render() {

const {user} = this.props

// 如果用户信息已完善, 自动跳转到大神主界面

if (user.header) {

return <Redirect to='/dashen'/>

}

return (

<div>

<NavBar>大神信息完善NavBar>

<HeaderSelector setHeader={this.setHeader}/>

<InputItem onChange={val => this.handleChange('post', val)}>求职岗

:InputItem>

<TextareaItem title="个人介绍:"

rows={3}

onChange={val => this.handleChange('info', val)}/>

<Button type='primary' onClick={() => this.props.updateUser(this.state)}>保存

Button>

div>

)

}

}

export default connect(

state => ({user: state.user}),

{updateUser}

)(DashenInfo)H5 前端课程系列

———————————————————————————

51

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.12.9. main 组件: containers/main/main.jsx

/*

应用主界面路由组件

*/

import React, {Component} from 'react'

import {Switch, Route} from 'react-router-dom'

import Cookies from 'js-cookies'

import LaobanInfo from '../laoban-info/laoban-info'

import DashenInfo from '../dashen-info/dashen-info'

export default class Main extends Component {

render() {

// 如果浏览器中没有保存 userid cookie, 直接跳转到 login

const userid = Cookies.get('userid')

if (!userid) {

this.props.history.replace('/login')

return null

}

return (

<div>

<Switch>

<Route path='/laobaninfo' component={LaobanInfo}/>

<Route path='/dasheninfo' component={DashenInfo}/>

Switch>

div>

)

}

}

2.13. 搭建整体界面

2.13.2. 后台路由: routes/index.js

// 根据 cookie 获取对应的 user

router.get('/user', function (req, res) {H5 前端课程系列

———————————————————————————

52

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 取出 cookie 中的 userid

const userid = req.cookies.userid

if(!userid) {

return res.send({code: 1, msg: '请先登陆'})

}

// 查询对应的 user

UserModel.findOne({_id: userid}, filter, function (err, user) {

return res.send({code: 0, data: user})

})

})

2.13.3. ajax 请求模块: api/index.js

// 查看用户信息(根据 cookie)

export const reqUser = () => ajax('/user')

2.13.4. redux 管理状态

1) redux/actions.js

/*

异步获取用户

*/

export const getUser = () => {

return async dispatch => {

const response = await reqUser()

const result = response.data

if (result.code === 0) {

dispatch(receiveUser(result.data))

} else {

dispatch(resetUser(result.msg))

}

}

}H5 前端课程系列

———————————————————————————

53

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.13.5. main 路由的子路由

1) containers/laoban/laoban.jsx

/*

老板的主路由组件

*/

import React, {Component} from 'react'

export default class Laoban extends Component {

render() {

return (

<div>老板列表div>

)

}

}

2) containers/dashen/dashen.jsx

/*

大神的主路由组件

*/

import React, {Component} from 'react'

export default class Dashen extends Component {

render() {

return (

<div>大神列表div>

)

}

}

3) containers/message/message.jsx

/*

对话消息列表组件

*/H5 前端课程系列

———————————————————————————

54

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import React, {Component} from 'react'

export default class Message extends Component {

render() {

return (

<div>Message 列表div>

)

}

}

4) containers/personal/personal.jsx

/*

用户个人中心路由组件

*/

import React, {Component} from 'react'

export default class Personal extends Component {

render() {

return (

<div>个人中心div>

)

}

}

5) components/not-found/not-found.jsx

/*

提示找不到页面的 UI 路由组件

*/

import React from "react"

import {Button} from "antd-mobile"

class NotFound extends React.Component {

render() {

return (

<div>

<div>

<h2>抱歉,找不到该页面!h2>H5 前端课程系列

———————————————————————————

55

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

<Button

type="primary"

onClick={() => this.props.history.replace("/")}

>

回到首页

Button>

div>

div>

)

}

}

export default NotFound

2.13.6. 底部导航组件

1) 相关图标图片

2) 组件: components/nav-footer/nav-footer.jsx

/*

底部导航的 UI 组件

*/

import React from 'react'

import PropTypes from 'prop-types'

import {TabBar} from 'antd-mobile'

import {withRouter} from 'react-router-dom'

const Item = TabBar.ItemH5 前端课程系列

———————————————————————————

56

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

class NavFooter extends React.Component {

static propTypes = {

navList: PropTypes.array.isRequired

}

render() {

// nav.hide = true/false hide 代表当前项应该被隐藏

const navList = this.props.navList.filter(nav => !nav.hide) // 回调函数返回值为 true,

当前元素就会留下, 否则不留

// 当前请求的路径

const {pathname} = this.props.location

return (

<TabBar>

{

navList.map((nav, index) => (

<Item key={nav.path}

title={nav.text}

icon={{uri: require(`./imgs/${nav.icon}.png`)}}

selectedIcon={{uri: require(`./imgs/${nav.icon}-selected.png`)}}

selected={pathname===nav.path}

onPress={() => {

this.props.history.replace(nav.path)

}}

/>

))

}

TabBar>

)

}

}

export default withRouter(NavFooter) // 让非路由组件可以访问到路由组件的 API

2.13.7. 应用主界面路由组件

1) containers/main/main.jsx

/*

应用主界面路由组件

*/H5 前端课程系列

———————————————————————————

57

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import React from 'react'

import {Route, Switch, Redirect} from 'react-router-dom'

import Cookies from 'js-cookie' // get(), set()

import {connect} from 'react-redux'

import {NavBar} from 'antd-mobile'

import LaobanInfo from '../laoban-info/laoban-info'

import DashenInfo from '../dashen-info/dashen-info'

import Dashen from '../dashen/dashen'

import Laoban from '../laoban/laoban'

import Message from '../message/message'

import Personal from '../personal/personal'

import NotFound from '../../components/not-found/not-found'

import NavFooter from '../../components/nav-footer/nav-footer'

import {getUser} from '../../redux/actions'

import {getRedirectPath} from '../../utils'

class Main extends React.Component {

// 组件类和组件对象

// 给组件对象添加属性

navList = [

{

path: '/laoban', // 路由路径

component: Laoban,

title: '大神列表',

icon: 'dashen',

text: '大神',

},

{

path: '/dashen', // 路由路径

component: Dashen,

title: '老板列表',

icon: 'laoban',

text: '老板',

},

{

path: '/message', // 路由路径

component: Message,

title: '消息列表',

icon: 'message',

text: '消息',H5 前端课程系列

———————————————————————————

58

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

},

{

path: '/personal', // 路由路径

component: Personal,

title: '用户中心',

icon: 'personal',

text: '个人',

}

]

componentDidMount() {

// cookie 中有 userid

// redux 中的 user 是空对象

const userid = Cookies.get('userid')

const {user} = this.props

if (userid && !user._id) {

this.props.getUser() // 获取 user 并保存到 redux

}

}

render() {

// 得到当前请求的 path

const pathname = this.props.location.pathname

// 判断用户是否已登陆()(cookie userid 是否有值)

const userid = Cookies.get('userid')

if (!userid) { // 如果没值, 自动跳转到登陆界面

return <Redirect to='/login'/>

}

// cookie 中有 userid

// redux 中的 user 是否有数据

const {user} = this.props

if (!user._id) {

return null // 不做任何显示

} else {

// 请求根路径时, 自动 跳转到对应的用户主界面

if (pathname === '/') {

const path = getRedirectPath(user.type, user.header)

return <Redirect to={path}/>

}

// 指定哪个 nav 应该被隐藏H5 前端课程系列

———————————————————————————

59

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

if (user.type === 'laoban') {

this.navList[1].hide = true

} else {

this.navList[0].hide = true

}

}

// 得到当前的 nav

const currentNav = this.navList.find(nav => nav.path === pathname)

return (

<div>

{currentNav ? <NavBar className='stick-top'>{currentNav.title}NavBar> : null}

<Switch>

<Route path='/laobaninfo' component={LaobanInfo}>Route>

<Route path='/dasheninfo' component={DashenInfo}>Route>

<Route path='/dashen' component={Dashen}>Route>

<Route path='/laoban' component={Laoban}>Route>

<Route path='/message' component={Message}>Route>

<Route path='/personal' component={Personal}>Route>

<Route component={NotFound}>Route>

Switch>

{currentNav ? <NavFooter unReadCount={this.props.unReadCount}

navList={this.navList}/> : null}

div>

)

}

}

export default connect(

state => ({user: state.user}),

{getUser}

)(Main)

2) 样式: assets/css/index.less

.am-tab-bar{ /*使导航始终在底部*/

position: fixed;

bottom: 0;

width: 100%;H5 前端课程系列

———————————————————————————

60

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

}

2.14. 个人中心功能

2.14.1. 个人中心组件: containers/personal/personal.jsx

1) 静态组件

/*

用户个人中心路由组件

*/

import React from 'react'

import {Result, List, WhiteSpace, Button} from 'antd-mobile'

const Item = List.Item

const Brief = Item.Brief

export default class Personal extends React.Component {H5 前端课程系列

———————————————————————————

61

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

render() {

return (

<div>

<Result

img={<img src={require(`../../assets/imgs/头像 1.png`)} style={{width: 50}}

alt="header"/>}

title='张三'

message='IBM'

/>

<List renderHeader={() => '相关信息'}>

<Item multipleLine>

<Brief>职位: 前端工程师Brief>

<Brief>简介: React/Vue/jQueryBrief>

<Brief>薪资: 20kBrief>

Item>

List>

<WhiteSpace/>

<List>

<Button type='warning'>退出登录Button>

List>

div>

)

}

}

2) 动态组件

/*

用户个人中心路由组件

*/

import React from 'react'

import {Result, List, WhiteSpace, Modal, Button} from 'antd-mobile'

import {connect} from 'react-redux'

import Cookies from 'js-cookie'

import {resetUser} from '../../redux/actions'

const Item = List.Item

const Brief = Item.BriefH5 前端课程系列

———————————————————————————

62

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

class Personal extends React.Component {

handleLogout = () => {

Modal.alert('退出', '确认退出登录吗?', [

{

text: '取消',

onPress: () => console.log('cancel')

},

{

text: '确认',

onPress: () => {

// 清除 cookie 中的 userid

Cookies.remove('userid')

// 重置 redux 中的 user 状态

this.props.resetUser()

}

}

])

}

render() {

const {username, header, post, info, salary, company} = this.props.user

return (

<div style={{marginTop: 50}}>

<Result

img={<img src={require(`../../assets/imgs/${header}.png`)} style={{width:

50}} alt="header"/>}

title={username}

message={company}

/>

<List renderHeader={() => '相关信息'}>

<Item multipleLine>

<Brief>职位: {post}Brief>

<Brief>简介: {info}Brief>

{salary ? <Brief>薪资: {salary}Brief> : null}

Item>

List>

<WhiteSpace/>

<List>H5 前端课程系列

———————————————————————————

63

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

<Button onClick={this.handleLogout} type='warning'>退出登录Button>

List>

div>

)

}

}

export default connect(

state => ({user: state.user}),

{resetUser}

)(Personal)

2.14.2. 样式: assets/css/index.less

.am-tab-bar{ /*使导航始终在底部*/

position: fixed;

bottom: 0;

width: 100%;

height: inherit; /*高度包裹内容*/

}

2.15. 老板/大神列表功能H5 前端课程系列

———————————————————————————

64

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.15.1. 后台路由: routes/index.js

/*

查看用户列表

*/

router.get('/list',function(req, res){

const { type } = req.query

UserModel.find({type},function(err,users){

return res.json({code:0, data: users})

})

})

2.15.2. ajax 请求模块: api/index.js

// 请求获取用户列表

export const reqUserList = (type) => ajax('/list', {type})

2.15.3. redux 管理状态

1) redux/action-types.js

export const RECEIVE_USER_LIST = 'receive_user_list' // 接收用户列表

2) redux/actions.js

import {

RECEIVE_USER_LIST

} from './action-types'

import {

reqUserList

} from '../api'

// 用户列表

const receiveUserList = (users) => ({type: RECEIVE_USER_LIST, data: users})

// 异步获取用户列表

export const getUserList = (type) => {

return async dispatch => {

const response = await reqUserList(type)H5 前端课程系列

———————————————————————————

65

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

const result = response.data

if (result.code === 0) {

dispatch(receiveUserList(result.data))

}

}

}

3) redux/reducers.js

const initUserList = []

function userList(state = initUserList, action) {

switch (action.type) {

case RECEIVE_USER_LIST:

return action.data

default:

return state

}

}

export default combineReducers({

user,

userList

})

2.15.4. 用户列表组件: components/user-list/user-list.jsx

1) 静态组件

/*

用户列表的 UI 组件

*/

import React from 'react'

import {Card, WingBlank, WhiteSpace} from 'antd-mobile'

const Header = Card.Header

const Body = Card.Body

class UserList extends React.Component {H5 前端课程系列

———————————————————————————

66

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

render() {

return (

<WingBlank>

<div>

<WhiteSpace/>

<Card>

<Header

thumb={require(`../../assets/imgs/头像 1.png`)}

extra='aa'

/>

<Body>

<div>职位: 前端工程师div>

<div>公司: Googlediv>

<div>月薪: 18Kdiv>

<div>描述: React/Vuediv>

Body>

Card>

div>

WingBlank>

)

}

}

export default UserList

2) 动态组件

/*

用户列表的 UI 组件

*/

import React from 'react'

import PropTypes from 'prop-types'

import {Card, WingBlank, WhiteSpace} from 'antd-mobile'

const Header = Card.Header

const Body = Card.Body

class UserList extends React.Component {

static propsTypes = {

userList: PropTypes.array.isRequired

}H5 前端课程系列

———————————————————————————

67

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

render() {

return (

<WingBlank>

{

this.props.userList.map(user => (

<div key={user._id}>

<WhiteSpace/>

<Card>

<Header

thumb={user.header ?

require(`../../assets/imgs/${user.header}.png`) : null}

extra={user.username}

/>

<Body>

<div>职位: {user.post}div>

{user.company ? <div>公司: {user.company}div> : null}

{user.salary ? <div>月薪: {user.salary}div> : null}

<div>描述: {user.info}div>

Body>

Card>

div>

))

}

WingBlank>

)

}

}

export default UserList

2.15.5. 老板路由组件: containers/laoban/laoban.jsx

/*

老板的主路由组件

*/

import React from 'react'

import {connect} from 'react-redux'

import {getUserList} from '../../redux/actions'H5 前端课程系列

———————————————————————————

68

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import UserList from '../../components/user-list/user-list'

class Laoban extends React.Component {

componentDidMount() {

this.props.getUserList('dashen')

}

render() {

return <UserList userList={this.props.userList}>UserList>

}

}

export default connect(

state => ({userList: state.userList}),

{getUserList}

)(Laoban)

2.15.6. 大神路由组件: containers/dashen/dashen.jsx

/*

大神的主路由组件

*/

import React from 'react'

import {connect} from 'react-redux'

import {getUserList} from '../../redux/actions'

import UserList from '../../components/user-list/user-list'

class Dashen extends React.Component {

componentDidMount() {

this.props.getUserList('laoban')

}

render() {

return <UserList userList={this.props.userList}>UserList>

}

}H5 前端课程系列

———————————————————————————

69

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

export default connect(

state => ({userList: state.userList}),

{getUserList}

)(Dashen)

2.15.7. 优化头部和底部的布局效果

1) 问题:

列表滑动, 顶部会跟着滑动而不可见

底部导航会遮挡列表的部分显示

2) Index.less

.stick-top { /*固定在顶部*/

z-index: 10;

position: fixed;

top: 0;

width: 100%;

}

3) main.jsx

<NavBar className='stick-top'>{currentNav.title}NavBar>

4) user-list.jsx

<WingBlank style={{marginTop: 50, marginBottom: 50}}>

2.16. 实时聊天功能

2.16.1. 下载相关依赖包

npm install --save socket.ioH5 前端课程系列

———————————————————————————

70

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.16.2 socket.io 介绍和使用

1) 介绍

socket.io 是一个能实现多人远程实时通信(聊天)的库

它包装的是H5 WebSocket和轮询, 如果是较新的浏览器内部使用WebSocket,

如果浏览器不支持, 那内部就会使用轮询实现实时通信

学习资料

https://socket.io/get-started/chat/

http://blog.csdn.net/neuq_zxy/article/details/77531126

2) 编码例子

服务器端

socketIO/test.js

module.exports = function (server) {

// 得到 IO 对象

const io = require('socket.io')(server)

// 监视连接(当有一个客户连接上时回调)

io.on('connection', function (socket) {

console.log('soketio connected')

// 绑定 sendMsg 监听, 接收客户端发送的消息

socket.on('sendMsg', function (data) {

console.log('服务器接收到浏览器的消息', data)

// 向客户端发送消息(名称, 数据)

io.emit('receiveMsg', data.name + '_' + data.date)

console.log('服务器向浏览器发送消息', data)

})

})

}

bin/ww

// start up socketio server

require('../socketIO/test')(server)

客户端

src/test/socketio_test.js

// 引入客户端 io

import io from 'socket.io-client'H5 前端课程系列

———————————————————————————

71

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 连接服务器, 得到代表连接的 socket 对象

const socket = io('ws://localhost:4000')

// 绑定'receiveMessage'的监听, 来接收服务器发送的消息

socket.on('receiveMsg', function (data) {

console.log('浏览器端接收到消息:', data)

})

// 向服务器发送消息

socket.emit('sendMsg', {name: 'Tom', date: Date.now()})

console.log('浏览器端向服务器发送消息:', {name: 'Tom', date: Date.now()})

src/index.js

import './test/socketio_test'

2.16.3. 后台实现

1) 添加新的数据库集合模型: db/models.js

// 定义 chats 集合的文档结构

const chatSchema = mongoose.Schema({

from: {type: String, required: true}, // 发送用户的 id

to: {type: String, required: true}, // 接收用户的 id

chat_id: {type: String, required: true}, // from to 组成的字符串

content: {type: String, required: true}, // 内容

read: {type:Boolean, default: false}, // 标识是否已读

create_time: {type: Number} // 创建时间

})

// 定义能操作 chats 集合数据的 Model

const ChatModel = mongoose.model('chat', chatSchema)

// 向外暴露 Model

exports.ChatModel = ChatModelH5 前端课程系列

———————————————————————————

72

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2) 新增路由: routes/index.js

// 引入 UserModel,ChatModel

const models = require('../db/models')

const UserModel = models.UserModel

const ChatModel = models.ChatModel

/*

获取当前用户所有相关聊天信息列表

*/

router.get('/msglist', function (req, res) {

// 获取 cookie 中的 userid

const userid = req.cookies.userid

// 查询得到所有 user 文档数组

UserModel.find(function (err, userDocs) {

// 用对象存储所有 user 信息: key user _id, val name header 组成的 user 对象

const users = {} // 对象容器

userDocs.forEach(doc => {

users[doc._id] = {username: doc.username, header: doc.header}

})

/*

查询 userid 相关的所有聊天信息

参数 1: 查询条件

参数 2: 过滤条件

参数 3: 回调函数

*/

ChatModel.find({'$or': [{from: userid}, {to: userid}]}, filter, function (err,

chatMsgs) {

// 返回包含所有用户和当前用户相关的所有聊天消息的数据

res.send({code: 0, data: {users, chatMsgs}})

})

})

})

/*

修改指定消息为已读

*/

router.post('/readmsg', function (req, res) {

// 得到请求中的 from to

const from = req.body.from

const to = req.cookies.useridH5 前端课程系列

———————————————————————————

73

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

/*

更新数据库中的 chat 数据

参数 1: 查询条件

参数 2: 更新为指定的数据对象

参数 3: 是否 1 次更新多条, 默认只更新一条

参数 4: 更新完成的回调函数

*/

ChatModel.update({from, to, read: false}, {read: true}, {multi: true}, function (err,

doc) {

console.log('/readmsg', doc)

res.send({code: 0, data: doc.nModified}) // 更新的数量

})

})

3) 使用上 socket.io 实现实时通信

server_socket.js

/*

启动 socket.io 服务的函数

*/

module.exports = function (server) {

// 引入操作 chats 集合数据的 Model

const ChatModel = require('./db/models').ChatModel

// 得到操作服务器端 sokectIO io 对象

const io = require('socket.io')(server)

// 绑定监听回调: 客户端连接上服务器

io.on('connection', function(socket) { // socket 代表连接

console.log('有客户端连接上了服务器')

// 绑定 sendMsg 监听, 接收客户端发送的消息

socket.on('sendMessage', function({from, to, content}) {

console.log('服务器接收到数据', {from, to, content})

// 将接收到的消息保存到数据库

const chat_id = [from, to].sort().join('_')

const create_time = Date.now()

const chatModel = new ChatModel({chat_id, from, to, create_time, content})

chatModel.save(function (err, chatMsg) {

// 保存完成后, 向所有连接的客户端发送消息

io.emit('receiveMessage', chatMsg) // 全局发送, 所有连接的客户端都可以收到H5 前端课程系列

———————————————————————————

74

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

console.log('向所有连接的客户端发送消息', chatMsg)

})

})

})

}

bin/www

// use server socket

require('../server_socket')(server)

2.16.4. ajax 请求模块: api/index.js

// 请求获取当前用户的所有聊天记录

export const reqChatMsgList = () => ajax('/msglist')

// 标识查看了指定用户发送的聊天信息

export const reqReadChatMsg = (from) => ajax('/readmsg', {from}, 'POST')

2.16.5. redux 管理状态

1) redux/action-types.js

export const RECEIVE_MSG_LIST = 'receive_msg_list' // 接收消息列表

export const RECEIVE_MSG = 'receive_msg' // 接收一条消息

export const MSG_READ = 'msg_read' // 标识消息已读

2) redux/actions.js

import io from 'socket.io-client'

import {

RECEIVE_MSG_LIST,

RECEIVE_MSG,

MSG_READ

} from './action-types'

import {

reqChatMsgList,

reqReadChatMsgH5 前端课程系列

———————————————————————————

75

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

} from '../api'

// 接收消息列表的同步 action

const receiveMsgList = ({users, chatMsgs, userid}) => ({type: RECEIVE_MSG_LIST, data:

{users, chatMsgs, userid}})

// 接收消息的同步 action

const receiveMsg = (chatMsg, isToMe) => ({type: RECEIVE_MSG, data: {chatMsg, isToMe}})

// 读取了消息的同步 action

const msgRead = ({from, to, count}) => ({type: MSG_READ, data: {from, to, count}})

/*

初始化客户端 socketio

1. 连接服务器

2. 绑定用于接收服务器返回 chatMsg 的监听

*/

function initIO(dispatch, userid) {

if(!io.socket) {

io.socket = io('ws://localhost:4000')

io.socket.on('receiveMsg', (chatMsg) => {

if(chatMsg.from===userid || chatMsg.to===userid) {

dispatch(receiveMsg(chatMsg, chatMsg.to === userid))

}

})

}

}

/*

获取当前用户相关的所有聊天消息列表

(在注册/登陆/获取用户信息成功后调用)

*/

async function getMsgList(dispatch, userid) {

initIO(dispatch, userid)

const response = await reqChatMsgList()

const result = response.data

if(result.code===0) {

const {chatMsgs, users} = result.data

dispatch(receiveMsgList({chatMsgs, users, userid}))

}

}

/*H5 前端课程系列

———————————————————————————

76

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

发送消息的异步 action

*/

export const sendMsg = ({from, to, content}) => {

return async dispatch => {

io.socket.emit('sendMsg', {from, to, content})

}

}

/*

更新读取消息的异步 action

*/

export const readMsg = (userid) => {

return async (dispatch, getState) => {

const response = await reqReadChatMsg(userid)

const result = response.data

if(result.code===0) {

const count = result.data

const from = userid

const to = getState().user._id

dispatch(msgRead({from, to, count}))

}

}

}

3) redux/reducers.js

import {

RECEIVE_MSG_LIST,

RECEIVE_MSG,

MSG_READ

} from "./action-types"

// 初始 chat 对象

const initChat = {

chatMsgs: [], // 消息数组 [{from: id1, to: id2}{}]

users: {}, // 所有用户的集合对象{id1: user1, id2: user2}

unReadCount: 0 // 未读消息的数量

}H5 前端课程系列

———————————————————————————

77

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 管理聊天相关信息数据的 reducer

function chat(state=initChat, action) {

switch (action.type) {

case RECEIVE_MSG:

var {chatMsg, userid} = action.data

return {

chatMsgs: [...state.chatMsgs, chatMsg],

users: state.users,

unReadCount: state.unReadCount + (!chatMsg.read && chatMsg.to===userid ? 1 : 0)

}

case RECEIVE_MSG_LIST:

var {chatMsgs, users, userid} = action.data

return {

chatMsgs,

users,

unReadCount: chatMsgs.reduce((preTotal, msg) => { // 别人发给我的未读消息

return preTotal + (!msg.read&&msg.to===userid ? 1 : 0)

}, 0)

}

case MSG_READ:

const {count, from, to} = action.data

return {

chatMsgs: state.chatMsgs.map(msg => {

if(msg.from===from && msg.to===to && !msg.read) {

// msg.read = true // 不能直接修改状态

return {...msg, read: true}

} else {

return msg

}

}),

users: state.users,

unReadCount: state.unReadCount-count

}

default:

return state

}

}

// 向外暴露整合所有 reducer 函数的结果

export default combineReducers({ // 返回的依然是一个 reducer 函数

user,

userList,H5 前端课程系列

———————————————————————————

78

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

chat

})

2.16.6. 聊天组件: containers/chat/chat.jsx

1) 静态组件

/*

对话聊天的路由组件

*/

import React, {Component} from 'react'

import {NavBar, List, InputItem} from 'antd-mobile'

const Item = List.Item

export default class Chat extends Component {

render() {

return (

<div id='chat-page'>

<NavBar>aaNavBar>

<List>

<Item

thumb={require('../../assets/imgs/头像 1.png')}

>

你好

Item>

<Item

thumb={require('../../assets/imgs/头像 1.png')}

>

你好 2

Item>

<Item

className='chat-me'

extra=''

>

很好

Item>H5 前端课程系列

———————————————————————————

79

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

<Item

className='chat-me'

extra=''

>

很好 2

Item>

List>

<div className='am-tab-bar'>

<InputItem

placeholder="请输入"

extra={

<span>发送span>

}

/>

div>

div>

)

}

}

css/index.less

/*消息文本居右*/

#chat-page .chat-me .am-list-extra {

flex-basis: auto; /*宽度包裹内容*/

}

#chat-page .chat-me .am-list-content {

padding-right: 15px;

text-align: right;

}

2) 动态组件

/*

对话聊天的路由组件

*/

import React, {Component} from 'react'

import {NavBar, List, InputItem, Icon} from 'antd-mobile'

import {connect} from 'react-redux'H5 前端课程系列

———————————————————————————

80

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import {sendMsg} from '../../redux/actions'

const Item = List.Item

class Chat extends Component {

state = {

content: ''

}

submit = () => {

const content = this.state.content.trim()

const to = this.props.match.params.userid

const from = this.props.user._id

this.props.sendMsg({from, to, content})

this.setState({content: ''})

}

render() {

const {user} = this.props

const {chatMsgs, users} = this.props.chat

const targetId = this.props.match.params.userid

if(!users[targetId]) {

return null

}

const meId = user._id

const chatId = [targetId, meId].sort().join('_')

const msgs = chatMsgs.filter(msg => msg.chat_id===chatId)

const targetIcon = users[targetId] ?

require(`../../assets/imgs/${users[targetId].header}.png`) : null

return (

<div id='chat-page'>

<NavBar

className='stick-top'

icon={<Icon type='left'/>}

onLeftClick={() => this.props.history.goBack()}

>

{users[targetId].username}

NavBar>

<List style={{marginBottom:50, marginTop:50}}>

{

msgs.map(msg => {H5 前端课程系列

———————————————————————————

81

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

if(msg.from===targetId) {

return (

<Item

key={msg._id}

thumb={targetIcon}

>

{msg.content}

Item>

)

} else {

return (

<Item

key={msg._id}

className='chat-me'

extra=''

>

{msg.content}

Item>

)

}

})

}

List>

<div className='am-tab-bar'>

<InputItem

placeholder="请输入"

value={this.state.content}

onChange={val => this.setState({content: val})}

extra={

<span onClick={this.submit}>发送span>

}

/>

div>

div>

)

}

}

export default connect(

state => ({user: state.user, chat: state.chat}),

{sendMsg}H5 前端课程系列

———————————————————————————

82

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

)(Chat)

3) 列表自动滑到底部显示

componentDidMount() {

// 初始显示列表

window.scrollTo(0, document.body.scrollHeight)

}

componentDidUpdate () {

// 更新显示列表

window.scrollTo(0, document.body.scrollHeight)

}

4) 表情功能

本质就是一个字符文本, 可以作用为字符串直接使用, 各个操作系统能显示

在线可用表情: https://emojipedia.org/

state = {

content: '', //输入的聊天内容

isShow: false // 是否显示表情列表

}

componentWillMount () {

this.emojis = ['', '', '', '', '', '', '', '', '', '', '', '', '', '',

'', '', '', '', '', '', '', '', '', '', '', '', '', '',

'', '', '', '', '', '', '', '', '', '', '', '', '', '']

this.emojis = this.emojis.map(value => ({text: value}))

// console.log(this.emojis)

}

// 切换表情列表的显示

toggleShow = () => {

const isShow = !this.state.isShow

this.setState({isShow})

if(isShow) {

// 异步手动派发 resize 事件,解决表情列表显示的 bugH5 前端课程系列

———————————————————————————

83

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

setTimeout(() => {

window.dispatchEvent(new Event('resize'))

}, 0)

}

}

<div className='am-tab-bar'>

<InputItem

placeholder="请输入"

value={this.state.content}

onChange={val => this.setState({content: val})}

onFocus={() => this.setState({isShow: false})}

extra={

<span>

<span onClick={this.toggleShow}

style={{marginRight: 10}}>span>

<span onClick={this.submit}>发送span>

span>

}

/>

{

this.state.isShow ? (

<Grid

data={this.emojis}

columnNum={8}

carouselMaxRow={4}

isCarousel={true}

onClick={(item) => {

this.setState({content: this.state.content + item.text})

}}

/>

) : null

}

div>H5 前端课程系列

———————————————————————————

84

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

2.16.7. 消息列表组件: containers/message/message.jsx

1) 静态组件

/*

对话消息列表组件

*/

import React, {Component} from 'react'

import {connect} from 'react-redux'

import {List, Badge} from 'antd-mobile'

const Item = List.Item

const Brief = Item.Brief

class Message extends Component {

render() {

return (

<List>

<Item

extra={<Badge text={3}/>}

thumb={require(`../../assets/images/头像 1.png`)}

arrow='horizontal'

>

你好

<Brief>nr1Brief>

Item>

<Item

extra={<Badge text={0}/>}

thumb={require(`../../assets/images/头像 2.png`)}

arrow='horizontal'

>

你好 2

<Brief>nr2Brief>

Item>

List>

)

}

}H5 前端课程系列

———————————————————————————

85

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

export default Message

2) 动态组件

/*

对话消息列表组件

*/

import React, {Component} from 'react'

import {connect} from 'react-redux'

import {List, Badge} from 'antd-mobile'

const Item = List.Item

const Brief = Item.Brief

/*

得到所有聊天的最后 msg 组成的数组

[msg1, msg2, msg3..]

// 1. 使用{}进行分组(chat_id), 只保存每个组最后一条 msg: {chat_id1: lastMsg1, chat_id2:

lastMsg2}

// 2. 得到所有分组的 lastMsg 组成数组: Object.values(lastMsgsObj) [lastMsg1, lastMsg2]

// 3. 对数组排序(create_time, 降序)

*/

function getLastMsgs(chatMsgs, userid) {

// 1. 使用{}进行分组(chat_id), 只保存每个组最后一条 msg: {chat_id1: lastMsg1,

chat_id2: lastMsg2}

const lastMsgsObj = {}

chatMsgs.forEach(msg => {

msg.unReadCount = 0

// 判断当前 msg 对应的 lastMsg 是否存在

const chatId = msg.chat_id

const lastMsg = lastMsgsObj[chatId]

// 不存在

if(!lastMsg) {

// msg 保存为 lastMsg

lastMsgsObj[chatId] = msg

// 别人发给我的未读消息H5 前端课程系列

———————————————————————————

86

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

if(!msg.read && userid===msg.to) {

// 指定 msg 上的未读数量为 1

msg.unReadCount = 1

}

} else {// 存在

// 如果 msg 的创建时间晚于 lastMsg 的创建时间, 替换

if (msg.create_time>lastMsg.create_time) {

lastMsgsObj[chatId] = msg

// 将原有保存的未读数量保存到新的 lastMsg

msg.unReadCount = lastMsg.unReadCount

}

// 别人发给我的未读消息

if(!msg.read && userid===msg.to) {

// 指定 msg 上的未读数量为 1

msg.unReadCount++

}

}

})

// 2. 得到所有分组的 lastMsg 组成数组: Object.values(lastMsgsObj) [lastMsg1, lastMsg2]

const lastMsgs = Object.values(lastMsgsObj)

// 3. 对数组排序(create_time, 降序)

lastMsgs.sort(function (msg1, msg2) {

return msg2.create_time-msg1.create_time

})

return lastMsgs

}

class Message extends Component {

render() {

// 得到 props 中的 user chat

const {user, chat} = this.props

// 得到当前用户的 id

const meId = user._id

// 得到所用用户的集合对象 users 和所有聊天的数组

const {users, chatMsgs} = chat

// 得到所有聊天的最后消息的数组

const lastMsgs = getLastMsgs(chatMsgs, meId)H5 前端课程系列

———————————————————————————

87

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

return (

<List style={{marginTop:50, marginBottom: 50}}>

{

lastMsgs.map(msg => {

const targetId = msg.from === meId ? msg.to : msg.from

const targetUser = users[targetId]

const avatarImg = targetUser.avatar ?

require(`../../assets/imgs/${targetUser.avatar}.png`) : null

return (

<Item

key={msg._id}

extra={<Badge text={msg.unReadCount}/>}

thumb={avatarImg}

arrow='horizontal'

onClick={() => this.props.history.push(`/chat/${targetId}`)}

>

{msg.content}

<Brief>{targetUser.name}Brief>

Item>

)

})

}

List>

)

}

}

export default connect(

state => ({

user: state.user,

chat: state.chat

})

)(Message)

2.16.8. 应用主界面路由组件: containers/main/main.jsx

/*

应用主界面路由组件H5 前端课程系列

———————————————————————————

88

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

*/

import React from 'react'

import {Route, Switch, Redirect} from 'react-router-dom'

import Cookies from 'js-cookie' // get(), set()

import {connect} from 'react-redux'

import {NavBar} from 'antd-mobile'

import LaobanInfo from '../laoban-info/laoban-info'

import DashenInfo from '../dashen-info/dashen-info'

import Dashen from '../dashen/dashen'

import Laoban from '../laoban/laoban'

import Message from '../message/message'

import Personal from '../personal/personal'

import NotFound from '../../components/not-found/not-found'

import NavFooter from '../../components/nav-footer/nav-footer'

import Chat from '../chat/chat'

import {getUser} from '../../redux/actions'

import {getRedirectPath} from '../../utils'

class Main extends React.Component {

// 组件类和组件对象

// 给组件对象添加属性

navList = [

{

path: '/laoban', // 路由路径

component: Laoban,

title: '大神列表',

icon: 'dashen',

text: '大神',

},

{

path: '/dashen', // 路由路径

component: Dashen,

title: '老板列表',

icon: 'laoban',

text: '老板',

},

{

path: '/message', // 路由路径

component: Message,

title: '消息列表',H5 前端课程系列

———————————————————————————

89

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

icon: 'message',

text: '消息',

},

{

path: '/personal', // 路由路径

component: Personal,

title: '用户中心',

icon: 'personal',

text: '个人',

}

]

componentDidMount() {

// cookie 中有 userid

// redux 中的 user 是空对象

const userid = Cookies.get('userid')

const {user} = this.props

if (userid && !user._id) {

this.props.getUser() // 获取 user 并保存到 redux

}

}

render() {

// 得到当前请求的 path

const pathname = this.props.location.pathname

// 判断用户是否已登陆()(cookie userid 是否有值)

const userid = Cookies.get('userid')

if (!userid) { // 如果没值, 自动跳转到登陆界面

return <Redirect to='/login'/>

}

// cookie 中有 userid

// redux 中的 user 是否有数据

const {user} = this.props

if (!user._id) {

return null // 不做任何显示

} else {

// 请求根路径时, 自动 跳转到对应的用户主界面

if (pathname === '/') {

const path = getRedirectPath(user.type, user.header)

return <Redirect to={path}/>

}H5 前端课程系列

———————————————————————————

90

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

// 指定哪个 nav 应该被隐藏

if (user.type === 'laoban') {

this.navList[1].hide = true

} else {

this.navList[0].hide = true

}

}

// 得到当前的 nav

const currentNav = this.navList.find(nav => nav.path === pathname)

// 得到 props 中的 unReadCount

const unReadCount = this.props.unReadCount

return (

<div>

{currentNav ? <NavBar className='stick-top'>{currentNav.title}NavBar> : null}

<Switch>

<Route path='/laobaninfo' component={LaobanInfo}>Route>

<Route path='/dasheninfo' component={DashenInfo}>Route>

<Route path='/dashen' component={Dashen}>Route>

<Route path='/laoban' component={Laoban}>Route>

<Route path='/message' component={Message}>Route>

<Route path='/personal' component={Personal}>Route>

<Route path='/chat/:userid' component={Chat}>Route>

<Route component={NotFound}>Route>

Switch>

{currentNav ? <NavFooter className='stick-top' navList={this.navList}

unReadCount={unReadCount}/> : null}

div>

)

}

}

export default connect(

state => ({

user: state.user,

unReadCount: state.chat.unReadCount // 未读消息数量

}),H5 前端课程系列

———————————————————————————

91

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

{getUser}

)(Main)

2.16.9. 显示总未读消息数量: components/nav-footer/nav-footer.jsx

/*

底部导航的 UI 组件

*/

import React from 'react'

import PropTypes from 'prop-types'

import {TabBar} from 'antd-mobile'

import {withRouter} from 'react-router-dom'

const Item = TabBar.Item

class NavFooter extends React.Component {

static propTypes = {

navList: PropTypes.array.isRequired,

unReadCount: PropTypes.number.isRequired

}

render() {

// nav.hide = true/false hide 代表当前项应该被隐藏

const navList = this.props.navList.filter(nav => !nav.hide) // 回调函数返回值为 true,

当前元素就会留下, 否则不留

// 当前请求的路径

const {pathname} = this.props.location

return (

<TabBar>

{

navList.map((nav, index) => (

<Item key={nav.path}

badge={nav.path === '/message' ? this.props.unReadCount : 0}

title={nav.text}

icon={{uri: require(`./imgs/${nav.icon}.png`)}}

selectedIcon={{uri: require(`./imgs/${nav.icon}-selected.png`)}}

selected={pathname===nav.path}H5 前端课程系列

———————————————————————————

92

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

onPress={() => {

this.props.history.replace(nav.path)

}}

/>

))

}

TabBar>

)

}

}

export default withRouter(NavFooter) // 让非路由组件可以访问到路由组件的 API

2.16.10. 查看聊天后, 更新未读数量

chat.jsx

componentDidMount() {

// 初始显示列表

window.scrollTo(0, document.body.scrollHeight)

this.props.readMsg(this.props.match.params.userid)

}

componentWillUnmount() {

this.props.readMsg(this.props.match.params.userid)

}

2.16.11. 添加聊天的页面的动画效果

1) 下载依赖包

npm install --save rc-queue-anim

2) 在聊天组件使用: containers/chat/chat.jsxH5 前端课程系列

———————————————————————————

93

更多 Java –大数据 前端 –python 人工智能资料下载,可访问百度:尚硅谷官网

import QueueAnim from 'rc-queue-anim'

{/*alpha left right top bottom scale scaleBig scaleX scaleY*/}

<QueueAnim type='scale' delay={100}>

{chatMsgs.map(msg => {

const avatar = require(`../../assets/imgs/${users[msg.from].avatar}.png`)

if (msg.from === userid) {

return (

<Item

key={msg._id}

thumb={avatar}

>{msg.content}Item>

)

} else {

return (

<Item

key={msg._id}

extra={<img src={avatar}/>}

className='chat-me'

>{msg.content}Item>

)

}

})}

QueueAnim>

你可能感兴趣的:(html,js)