好客租房移动web项目(3)

1、WebSocket的Demo

1.1、App.js

// node中是通过net核心模块来开发websocket
// 市面上有很多基于net模块开发的第三方模块,开发websocket特别的简单。
const WebSocket = require('ws')

// 创建webSocket服务
const server = new WebSocket.Server({ port: 8080 })

// 启动websocket服务
// 只要浏览器端连接了websocket,connention事件就会触发
server.on('connection', client => {
  console.log('客户端连接上了')
  client.send('欢迎光临')

  // 每当客户端给我们发送消息的时候,message事件就会触发
  client.on('message', data => {
    let msg = ''
    switch (data) {
      case '你好':
        msg = '废话,当然好'
        break
      case '你是男的':
        msg = '不,我是小姐姐'
        break
      case '约吗':
        msg = '丑拒'
        break
      default:
        msg = '你说啥呀'
    }
    client.send(msg)
  })
})

1.2、index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <input type="text">
  <button>发送</button>

  <script>
    var input = document.querySelector('input')
    var button = document.querySelector('button')
    var ws = new WebSocket('ws://127.0.0.1:8080')
    ws.onopen = function () {
      button.onclick = function () {
        ws.send(input.value)
      }
    }

    ws.onmessage = function (e) {
      console.log('接收到服务器的消息', e.data)
    }
  </script>
</body>

</html>

2、好客租房移动web项目

2.1、components

 --- home

(1)chat

--Chat.css

.chat-container {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.chat-title {
  position: absolute;
  top: 0;
  width: 100%;
  height: 40px;
  line-height: 40px;
  background-color: #d4e0ee;
  text-align: center;
  font-size: 20px;
  z-index: 2;
}
.chat-list {
  box-sizing: border-box;
  height: 100%;
  width: 100%;
  padding-top: 40px;
  overflow-y: scroll;
}
.chat-list ul {
  margin: 0;
  padding: 0;
}
.chat-list li {
  position: relative;
  list-style: none;
  background-color: #f3f3f3;
  padding: 0 15px;
  margin-top: 2px;
  height: 70px;
  line-height: 70px;
  overflow: hidden;
}
.chat-list li img {
  width: 60px;
  height: 60px;
  margin-left: 5px;
  margin-top: 5px;
}
.chat-list li .name {
  position: absolute;
  left: 90px;
  top: -15px;
}
.chat-list li .info {
  position: absolute;
  left: 90px;
  top: 15px;
}
.chat-list li .time {
  position: absolute;
  right: 20px;
  top: -15px;
}
.chat-window {
  position: fixed;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: #f0f0f0;
  z-index: 99999;
}
.chat-list::-webkit-scrollbar {
  width: 3px;
}
.chat-list::-webkit-scrollbar-thumb {
  background-color: #cbdaea;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  min-height: 2rem;
  background-clip: padding-box;
  border-radius: 5px;
}
.chat-list::-webkit-scrollbar-track {
  background-color: #fff;
}
.chat-list::-webkit-scrollbar-track-piece {
  height: 30px;
}

--Chat.jsx

import React from 'react'
import './Chat.css'
import moment from 'moment'
import ChatWindow from './ChatWindow'
class Chat extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [],
      isShow: false,
      chatInfo: {}
    }
  }

  async componentDidMount() {
    let res = await this.axios.post('chats/list')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        list: data.list
      })
    }
  }

  toChat = item => {
    // console.log(item)
    this.setState({
      isShow: true,
      chatInfo: {
        from_user: item.from_user,
        to_user: item.to_user,
        avatar: item.avatar,
        username: item.username
      }
    })
  }

  closeWindow = () => {
    this.setState({
      isShow: false
    })
  }

  render() {
    return (
      <div className="chat-container">
        {this.state.isShow && (
          <ChatWindow
            closeWindow={this.closeWindow}
            chatInfo={this.state.chatInfo}
          />
        )}
        <div className="chat-title">聊天</div>
        <div className="chat-list">
          <ul>
            {this.state.list.map(item => (
              <li key={item.id} onClick={this.toChat.bind(this, item)}>
                <div className="avarter">
                  <img src={item.avatar} alt="avarter" />
                  <span className="name">{item.username}</span>
                  <span className="info">{item.chat_msg}</span>
                  <span className="time">
                    {moment(item.ctime).format('YYYY-MM-DD HH:mm:ss')}
                  </span>
                </div>
              </li>
            ))}
          </ul>
        </div>
      </div>
    )
  }
}
export default Chat

--ChatWindow.css

.chat-window-title {
  height: 40px;
  line-height: 40px;
  font-size: 20px;
  padding-top: 3px;
  background-color: #d4e0ee;
  position: absolute;
  width: 100%;
  text-align: center;
  box-sizing: border-box;
}
.chat-window-content {
  position: absolute;
  top: 40px;
  bottom: 126px;
  width: 100%;
  overflow-y: auto;
}
.chat-window-content ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.chat-window-content ul li {
  min-height: 60px;
  clear: both;
}
.chat-window-content ul li img {
  width: 50px;
  height: 50px;
  padding-top: 5px;
  padding-left: 5px;
  vertical-align: top;
  float: left;
}
.chat-window-content ul li span {
  display: inline-block;
  font-size: 20px;
  background-color: #fff;
  padding: 5px 10px;
  margin: 10px 60px 0 10px;
  width: 65%;
  min-height: 50px;
  float: left;
  box-sizing: border-box;
  word-break: break-all;
  word-wrap: break-word;
}
.chat-window-content ul li.chat-info-right img {
  float: right;
  padding-right: 5px;
}
.chat-window-content ul li.chat-info-right span {
  float: right;
  margin: 10px 6px 0 60px;
  background-color: lightgreen;
}
.chat-window-input {
  width: 100%;
  position: absolute;
  bottom: 0;
  background-color: #21b97a;
  min-height: 60px;
  text-align: right;
  padding: 5px;
}
.chat-ret-btn {
  position: absolute;
  left: 0;
  top: 8px;
}
.ui.form textarea {
  margin-bottom: 5px;
}

--ChatWindow.jsx

import React from 'react'
import { Icon, Form, Button, TextArea } from 'semantic-ui-react'
import './ChatWindow.css'
import handle from './wsmain'
class ChatWindow extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [],
      client: '',
      msgContent: '',
      fromAvatar: ''
    }
  }

  getChatList = async () => {
    // 发送ajax请求,获取聊天数据
    let res = await this.axios.post('chats/info', {
      from_user: this.props.chatInfo.from_user,
      to_user: this.props.chatInfo.to_user
    })
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        list: data.list
      })
    }
  }

  // 聊天窗口组件挂载的时候,获取聊天列表信息
  async componentDidMount() {
    // 获取当前用户的头像
    let res = await this.axios.post('my/info', {
      user_id: localStorage.getItem('uid')
    })
    let { meta, data } = res
    if (meta.status === 200) {
      // 通过websocket连接服务器,得到client对象
      let currentUser = localStorage.getItem('uid') - 0
      // 参数1: 连接聊天服务器的id
      // 参数2: 回调函数,服务器每次给发送的消息,都在data中
      // 返回值:client对象
      let client = handle(currentUser, data => {
        // 该回调函数用来处理服务器返回的消息(其实就是对方发送消息)
        // 其实就是接收对方返回的消息
        let newData = JSON.parse(data.content)
        let newList = [...this.state.list, newData]
        this.setState({
          list: newList
        })
      })
      this.setState({
        client: client,
        avatar: data.avatar
      })
    }
    this.getChatList()
  }

  // 给服务器发送数据
  sendMsg = () => {
    // 给服务器发送的数据包含:
    // from_user: 从谁发
    // to_user: 给谁发
    // this.state.msgContent: 发送的内容

    let pdata = {
      id: new Date().getTime(),
      from_user: this.props.chatInfo.from_user,
      to_user: this.props.chatInfo.to_user,
      chat_msg: this.state.msgContent,
      avatar: this.state.avatar
    }
    // 把消息发送出去
    this.state.client.emitEvent('msg_text_send', JSON.stringify(pdata))
    // 重新渲染聊天列表
    let newList = [...this.state.list, pdata]
    this.setState({
      list: newList,
      msgContent: ''
    })
  }

  handleChange = e => {
    this.setState({
      msgContent: e.target.value
    })
  }
  render() {
    let currentUser = localStorage.getItem('uid') - 0
    return (
      <div className="chat-window">
        <div className="chat-window-title">
          <Icon
            onClick={this.props.closeWindow}
            name="angle left"
            className="chat-ret-btn"
            size="large"
          />
          <span>{this.props.chatInfo.username}</span>
        </div>
        <div className="chat-window-content">
          <ul>
            {this.state.list.map(item => (
              // 通过currentUser与from_user去比较
              <li
                key={item.id}
                className={
                  currentUser === item.from_user
                    ? 'chat-info-right'
                    : 'chat-info-left'
                }
              >
                {/* 这个头像显示是错误 */}
                <img src={item.avatar} alt="" />
                <span>{item.chat_msg}</span>
              </li>
            ))}
          </ul>
        </div>
        <div className="chat-window-input">
          <Form>
            <TextArea
              placeholder="请输入内容..."
              value={this.state.msgContent}
              onChange={this.handleChange}
            />
            <Button onClick={this.props.closeWindow}>关闭</Button>
            <Button primary onClick={this.sendMsg}>
              发送
            </Button>
          </Form>
        </div>
      </div>
    )
  }
}

export default ChatWindow

--DataPacket.js

class DataPacket {
    constructor(message) {
        if(typeof(message) === "string"){
            try {
                this._data = JSON.parse(message);
            } catch (error) {
                this._data = undefined;
            }
            
        } else if(typeof(message) === "object"){
            this._data = message;
        }
    }

    get data() {
        return this._data
    }

    get content() {
        return this._data["content"]
    }

    get type() {
        return this._data["type"];
    }
    
    set type(type) {
        this._data["type"] = type;
    }

    get rawMessage() {
        return JSON.stringify(this._data);
    }
}

export default DataPacket

--DMClient.js

import DataPacket from './DataPacket.js'
import IMEvent from './IMEvent.js'

/**
 * 通讯客户端
 */
class IMClient {
  constructor(url) {
    this._url = url
    this._autoConnect = true
    this._handlers = {}
    this._DataPacketQueue = []
    this._isOpened = false

    this.addEventListener(IMEvent.CONNECTED, () => {
      this.serverOnConnected()
    })

    // this.addEventListener(IMEvent.CONNECTED, () => {
    //   this.clearMsgQueue();
    // })

    this.addEventListener(IMEvent.DISCONNECTED, () => {
      this.serverOnDisconnected()
    })
  }
  /**
   * 底层通讯函数回调
   */
  // 连接
  connect() {
    if (!this._socket) {
      this._socket = new WebSocket(this._url)

      this._socket.onmessage = evt => {
        this.onMessage(evt.data)
      }
      this._socket.onopen = ws => {
        this.onOpen(ws)
      }
      this._socket.onclose = ws => {
        this.onClose(ws)
      }
      this._socket.onerror = ws => {
        this.onError(ws)
      }
    }
  }

  // 消息接收
  onMessage(message) {
    // console.log(message, 11)
    if (typeof message === 'string') {
      // message就是服务器返回的消息
      this.dispatchMessage(message)
    }
  }

  // 打开回调
  onOpen() {
    this.emitEvent(IMEvent.CONNECTED)
  }

  // 关闭回调
  onClose() {
    this._socket = undefined
    this.emitEvent(IMEvent.DISCONNECTED)
    if (this.autoReconnect) {
      setTimeout(() => {
        this.connect()
      }, 5000)
    }
  }

  // 出现错误回调
  onError() {
    this._socket = undefined
    if (this.autoReconnect) {
      setTimeout(() => {
        this.connect()
      }, 5000)
    }
  }

  // 向服务器发送数据包
  sendDataPacket(dataPacket) {
    if (this._isOpened) {
      this._socket.send(dataPacket.rawMessage)
    } else {
      this._DataPacketQueue.push(dataPacket)
    }
  }

  /**
   * 事件处理
   */
  // 当连接服务器时处理
  serverOnConnected() {
    this._isOpened = true
  }

  // 当断开连接服务器时处理
  serverOnDisconnected() {
    this._isOpened = false
  }

  // 清空消息队列
  clearMsgQueue() {
    if (this._DataPacketQueue.length > 0) {
      this._DataPacketQueue.forEach(dataPacket => {
        this.sendDataPacket(dataPacket)
      })
      this._socketMsgQueue = []
    }
  }

  /**
   * 发射事件
   * @param {string} type 事件类型
   * @param {参数列表} args 传递事件参数
   */
  emitEvent(type, ...args) {
    if (this._handlers[type] && this._handlers[type].length > 0) {
      let handlers = this._handlers[type]
      for (var i = 0; i < handlers.length; i++) {
        handlers[i].call(this, ...args)
      }
    }
  }

  /**
   * 添加事件监听
   *
   * @param {string} type 事件类型
   * @param {Function} callback 事件处理函数
   */
  addEventListener(type, handler) {
    if (!this._handlers[type]) {
      this._handlers[type] = []
    }
    this._handlers[type].push(handler)
  }

  /**
   * 移除事件
   *
   * @param {string} type 事件类型
   * @param {*} callback
   */
  removeEventListener(type, handler) {
    if (this._handlers[type] && this._handlers[type].length > 0) {
      let handlers = this._handlers[type]
      for (var i = handlers.length - 1; i >= 0; i--) {
        if (handler === handlers[i]) {
          handlers.splice(i, 1)
        }
      }
    }
  }

  /**
   * 消息封装成数据包
   */
  dispatchMessage(message) {
    let dataPacket = new DataPacket(message)
    if (dataPacket.data) {
      this.dispatchDataPacket(dataPacket)
    }
  }

  /**
   * 处理数据包
   */
  dispatchDataPacket(dataPacket) {
    this.emitEvent(dataPacket.type, dataPacket)
  }
}

export default IMClient

--IMEvent.js

export default {
  ERROR: "error",
  CONNECTED: "connected",
  DISCONNECTED: "disconnected",
  MSG_DISCONNECTED: "msg_disconnected",
  MSG_TEXT_SEND: "msg_text_send",
  MSG_TEXT_REC: "msg_text_rec",
  USER_REG: "user_reg",
}

--wsmain.js

import IMEvent from './IMEvent.js'
import IMClient from './IMClient.js'
import DataPacket from './DataPacket.js'
// import config from '../../common.js';

const handle = (currentUser, handleMsg) => {
  const client = new IMClient('ws://127.0.0.1:8087');
  // 发送消息
  client.addEventListener(IMEvent.MSG_TEXT_SEND, data => {
    let dataPacket = new DataPacket({
      type: IMEvent.MSG_TEXT_SEND,
      content: data
    })
    client.sendDataPacket(dataPacket)
  })
  // 接收消息
  client.addEventListener(IMEvent.MSG_TEXT_REC, data => {
    handleMsg(data)
  })
  // 注册身份
  client.addEventListener(IMEvent.CONNECTED, () => {
    let dataPacket = new DataPacket({
      type: IMEvent.USER_REG,
      content: currentUser
    })
    client.sendDataPacket(dataPacket)
    console.log('success')
  })
  client.connect();
  return client;
}
export default handle;

(2)demo

--Demo.jsx

import React from 'react'
// 1. 导入相关的依赖包, 会自动导入依赖的核心样式
import Tloader from 'react-touch-loader'
import './style.less'
class Demo extends React.Component {
  constructor() {
    super()
    this.state = {
      hasMore: false,
      initializing: 1,
      total: 0 // 总条数
    }
  }

  refresh = (resolve, reject) => {
    setTimeout(() => {
      // 获取第一页的数据
      this.setState({
        total: 10,
        hasMore: true
      })
      resolve()
    }, 1000)
  }

  loadMore = (resolve, reject) => {
    setTimeout(() => {
      let newTotal = this.state.total + 10
      this.setState({
        total: newTotal,
        hasMore: newTotal > 0 && newTotal < 50
      })
      resolve()
    }, 1000)
  }

  componentDidMount() {
    // 后台加载数据
    setTimeout(() => {
      this.setState({
        total: 10,
        initializing: 2,
        hasMore: true
      })
    }, 1000)
  }

  render() {
    let { hasMore, initializing, total } = this.state
    let data = []
    for (var i = 0; i < total; i++) {
      data.push(
        <li key={i}>
          <p>{i}</p>
        </li>
      )
    }
    return (
      <div className="view">
        {/* onRefresh: 下拉刷新功能 */}
        <Tloader
          className="main"
          initializing={initializing}
          hasMore={hasMore}
          onRefresh={this.refresh}
          onLoadMore={this.loadMore}
        >
          <ul>{data}</ul>
        </Tloader>
      </div>
    )
  }
}
export default Demo

--Demo-备份.jsx

import React from 'react'
import './style.less'

import Tloader from 'react-touch-loader'

class Demo extends React.Component {
  // 构造函数
  constructor() {
    super()
    this.state = {
      // 能够下拉刷新(能)
      canRefreshResolve: 1,
      // 总的数量
      listLen: 0,
      // 是否有更多的数据
      hasMore: 0,
      // 0: 不显示进度条 1:显示进度条 2.结束进度条
      initializing: 1,
      // 刷新的时间
      refreshedAt: Date.now()
    }
  }

  // 组件刚挂载执行的代码
  componentDidMount() {
    // 发送ajax请求,获取到数据
    setTimeout(() => {
      this.setState({
        // 获取到了9条数据
        listLen: 9,
        // hasMore: true, 有更多,显示加载更多
        hasMore: 1,
        // 结束进度条
        initializing: 2 // initialized
      })
    }, 2000)
  }

  // 下拉刷新的代码
  refresh = (resolve, reject) => {
    setTimeout(() => {
      const { canRefreshResolve } = this.state
      if (!canRefreshResolve) reject()
      else {
        // 重置了状态
        this.setState({
          listLen: 9,
          hasMore: 1,
          refreshedAt: Date.now()
        })

        resolve()
      }
    }, 2000)
  }
  // 加载更多的代码
  loadMore = resolve => {
    setTimeout(() => {
      const { listLen } = this.state
      const l = listLen + 9

      this.setState({
        listLen: l,
        hasMore: l > 0 && l < 50
      })

      resolve()
    }, 2000)
  }

  render() {
    const { listLen, hasMore, initializing, refreshedAt } = this.state
    const list = []

    if (listLen) {
      for (let i = 0; i < listLen; i++) {
        list.push(
          <li key={i}>
            <p>{i}</p>
          </li>
        )
      }
    }
    return (
      <div className="view">
        <Tloader
          className="main"
          onRefresh={this.refresh}
          onLoadMore={this.loadMore}
          hasMore={hasMore}
          initializing={initializing}
        >
          <ul>{list}</ul>
        </Tloader>
      </div>
    )
  }
}

export default Demo

--style.less

html,
body {
  margin: 0;
  padding: 0;
}
body {
  font-family: 'Helvetica Neue', Helvetica, 'STHeitiSC-Light',
    'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
  text-align: center;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
:focus {
  outline: none;
}
:not(input):not(textarea):not([contenteditable]) {
  -webkit-touch-callout: none;
  user-select: none;
}

html {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.view {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  overflow: hidden;
  background-color: #efeff4;
  display: flex;
  flex-direction: column;

  // header, footer
  h1,
  .footer {
    margin: 0;
    background-color: #333;
    color: #eee;
    font-size: 1em;
    line-height: 3;

    a {
      color: inherit;
      margin: 0 1em;
    }
  }

  .main {
    flex: 1;
    overflow-x: hidden;
    overflow-y: scroll;
    -webkit-overflow-scrolling: touch; // enhance ios scrolling

    ul {
      list-style: none;
      margin: 0;
      padding: 0;

      li {
        background-color: #fff;
        border-bottom: 1px solid #ccc;

        p {
          margin: 0;
          line-height: 3em;
        }
      }
    }
  }
}

(3)info

--Info.css

.find-container {
  position: absolute;
  width: 100%;
  height: 100%;
  background-color: lightgreen;
  overflow: hidden;
}
.find-topbar {
  position: absolute;
  width: 100%;
  height: 40px;
  line-height: 40px;
  background-color: #b5efff;
  text-align: center;
  font-size: 20px;
  z-index: 999;
}
.find-content {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding-top: 40px;
  overflow-y: auto;
}

.ui.bottom.attached.segment.active.tab {
  top: 80px;
  bottom: 0px;
  position: absolute;
  overflow-y: auto;
}

.ui .view {
  background-color: #fff;
}

/* 消息的样式 */
.ui.items > .item > .content > .header.info-title {
  text-align: left;
  height: auto;
  filter: none;
  -webkit-filter: blur(0px);
  -moz-filter: blur(0px);
  -ms-filter: blur(0px);
  filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='0');
}

.ui.items > .item .meta {
  text-align: left;
}

/* 问答相关的样式 */
.info-ask-list {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: left;
}
.info-ask-list li {
  position: relative;
  margin: 5px 10px;
  padding: 10px 0;
}

.info-ask-list li .title {
  color: #000;
  font-weight: bold;
  padding: 5px;
  font-size: 16px;
}
.info-ask-list li .title .cate {
  box-sizing: border-box;
  background-color: #c6f3ff;
  margin-right: 5px;
  padding: 0px 3px;
  display: inline-block;
  font-size: 14px;
}
.info-ask-list li .user {
  font-size: 12px;
  color: gray;
  padding-left: 5px;
}
.info-ask-list li .info {
  font-size: 10px;
  color: gray;
  padding-left: 5px;
}
.info-ask-list li .tag {
  padding-left: 5px;
  margin-top: 10px;
  overflow: hidden;
}
.info-ask-list li .tag span {
  font-size: 12px;
  color: #000;
  border: 1px solid gray;
  margin-right: 5px;
  padding: 2px 5px;
}
.info-ask-list li .tag span:last-child {
  float: right;
  border: 0px;
}
.info-ask-btn {
  padding: 10px 15px;
}

--Info.jsx

import React from 'react'
import './Info.css'
import { Tab, Item, Icon } from 'semantic-ui-react'
import Tloader from 'react-touch-loader'
class Info extends React.Component {
  render() {
    const panes = [
      {
        menuItem: '资讯',
        render: () => (
          <Tab.Pane>
            <M1 />
          </Tab.Pane>
        )
      },
      {
        menuItem: '头条',
        render: () => (
          <Tab.Pane>
            <M2 />
          </Tab.Pane>
        )
      },
      {
        menuItem: '问答',
        render: () => (
          <Tab.Pane>
            <M3 />
          </Tab.Pane>
        )
      }
    ]
    return (
      <div className="find-container">
        <div className="find-topbar">资讯</div>
        <div className="find-content">
          <Tab panes={panes} />
        </div>
      </div>
    )
  }
}
export default Info

function M1() {
  return <Loader type="1" />
}
function M2() {
  return <Loader type="2" />
}
function M3() {
  return <Loader type="3" />
}

// 定义Message组件
function Message({ data }) {
  return (
    <Item.Group unstackable>
      {data.map(item => (
        <Item key={item.id}>
          <Item.Image size="small" src="http://47.96.21.88:8086/public/1.png" />
          <Item.Content verticalAlign="middle">
            <Item.Header className="info-title">{item.info_title}</Item.Header>
            <Item.Meta>
              <span className="price">$1200</span>
              <span className="stay">1 Month</span>
            </Item.Meta>
          </Item.Content>
        </Item>
      ))}
    </Item.Group>
  )
}

// 定义问答组件
function AskAnswer({ data }) {
  return (
    <ul className="info-ask-list">
      {data.map(item => (
        <li key={item.id}>
          <div className="title">
            <span className="cate">
              <Icon color="green" name="users" size="small" />
              思维
            </span>
            <span>
              你好你好你好你好你好你好你好你好你好你好你好你好你好你好
            </span>
          </div>
          <div className="user">
            <Icon circular name="users" size="mini" />
            张三的回答
          </div>
          <div className="info">
            你好你好你好你好你好你好你好你好你好你好你好你好你好你好
          </div>
          <div className="tag">
            <span>你好X</span>
            <span>你好X</span>
            <span>你好X</span>
            <span>123个回答</span>
          </div>
        </li>
      ))}
    </ul>
  )
}

// 定义touch-loader组件
class Loader extends React.Component {
  constructor() {
    super()
    this.state = {
      hasMore: false,
      initializing: 1,
      pagenum: 0,
      pagesize: 2,
      list: [],
      total: 0
    }
  }

  // 加载数据
  loadData = () => {
    // 发送ajax请求,动态加载数据
    // console.log(this.props.type)
    return this.axios
      .post('infos/list', {
        type: this.props.type,
        pagenum: this.state.pagenum,
        pagesize: this.state.pagesize
      })
      .then(res => {
        let { meta, data } = res
        if (meta.status === 200) {
          return data.list
        }
      })
  }
  // 需要在组件渲染的时候,动态的加载数据
  async componentDidMount() {
    let data = await this.loadData()
    let newNum = this.state.pagenum + this.state.pagesize
    this.setState({
      list: data.data,
      initializing: 2,
      toal: data.total,
      pagenum: newNum,
      hasMore: newNum < data.total
    })
  }

  refresh = async (resolve, reject) => {
    // 重置初始的条数
    // react的setState是异步的,通过setState修改react内部的数据,不是立即更新的
    // 如果就想获取立即更新的数据
    this.setState({
      pagenum: 0
    })
    setTimeout(async () => {
      let data = await this.loadData()
      let newNum = this.state.pagenum + this.state.pagesize
      this.setState({
        list: data.data,
        pagenum: newNum,
        hasMore: newNum < data.total
      })
      resolve()
    }, 0)
  }

  loadMore = async (resolve, reject) => {
    let data = await this.loadData()
    let newNum = this.state.pagenum + this.state.pagesize
    let newList = [...this.state.list, ...data.data]
    this.setState({
      list: newList,
      initializing: 2,
      toal: data.total,
      pagenum: newNum,
      hasMore: newNum < data.total
    })
    resolve()
  }

  render() {
    let { hasMore, initializing, list } = this.state
    let { type } = this.props
    return (
      <div className="view">
        <Tloader
          className="main"
          onRefresh={this.refresh}
          onLoadMore={this.loadMore}
          hasMore={hasMore}
          initializing={initializing}
        >
          {type === '3' ? <AskAnswer data={list} /> : <Message data={list} />}
        </Tloader>
      </div>
    )
  }
}

(4)main

--Main.css

.main {
  width: 100%;
  height: 100%;
  position: relative;
}
.search {
  height: 40px;
  line-height: 40px;
  position: absolute;
  width: 100%;
  top: 0;
  z-index: 999;
}

.content {
  width: 100%;
  height: 100%;
  padding-top: 40px;
  /* 给content添加了垂直的滚动条 */
  overflow-y: auto;
}

/* 滚动条样式 */
.content::-webkit-scrollbar {
  width: 3px;
}
.content::-webkit-scrollbar-thumb {
  background-color: #cbdaea;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  min-height: 2rem;
  background-clip: padding-box;
  border-radius: 5px;
}
.content::-webkit-scrollbar-track {
  background-color: #fff;
}

/* 菜单的样式 */
.home-menu-item {
  border-radius: 31px;
  height: 62px;
  line-height: 62px;
  color: #fff;
}

.ui.grid > .row > .column:nth-child(1) .home-menu-item {
  background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
  background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
  background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
  background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
  background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
  background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
  background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
  background-color: #7ccc39;
}

.ui.menu > .row > .column {
  text-align: center;
  margin-top: 10px;
}

/* 好客咨询样式 */
.home-msg {
  margin: 25px 0 15px 0;
  position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
  width: 50px !important;
  height: 50px !important;
  margin-left: 7px;
  margin-top: 2px;
}
.ui.items > .item > .content > .header {
  display: block;
  height: 25px;
  margin-top: 5px;
  font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
  color: orange;
  margin-right: 10px;
  overflow: hidden;
  display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: baseline;
  width: 65%;
}
.home-msg .home-msg-more {
  position: absolute;
  right: 5px;
  top: 12px;
}

/* 好客问答样式 */
.home-ask-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.home-ask ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.home-ask ul li {
  height: 70px;
  line-height: 70px;
}

.home-ask ul li div {
  height: 35px;
  line-height: 35px;
}

.home-ask ul li div:first-child {
  margin-left: 10px;
  font-size: 18px;
}
.home-ask ul li div:last-child {
  margin-left: 30px;
  position: relative;
}

.home-ask ul li div:last-child div {
  position: absolute;
  font-size: 14px;
  right: 15px;
  top: 0;
}

/* 房屋样式 */
.home-hire-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.ui.items > .item > .image {
  margin-left: 10px;
  margin-top: 6px;
}

.ui.items > .item > .content > .description:last-child {
  color: orange;
}

--Main.jsx

import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
  Input,
  Grid,
  Icon,
  Item,
  Button,
  Dimmer,
  Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      imgList: [],
      menuList: [],
      infoList: [],
      faqList: [],
      houseList: [],
      loading: true
    }
  }

  /* // 获取轮播图数据
  getImgList = async () => {
    let res = await this.axios.post('homes/swipe')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        imgList: data.list
      })
    }
  }
  // 获取菜单数据
  getMenuList = async () => {
    let res = await this.axios.post('homes/menu')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        menuList: data.list
      })
    }
  }
  // 获取咨询数据
  getInfoList = async () => {
    let res = await this.axios.post('homes/info')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        infoList: data.list
      })
    }
  }
  // 获取咨询数据
  getFaqList = async () => {
    let res = await this.axios.post('homes/faq')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        faqList: data.list
      })
    }
  }
  // 获取房屋数据
  getHouseList = async () => {
    let res = await this.axios.post('homes/house')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        houseList: data.list
      })
    }
  } */
  doRequest = (url, dataName) => {
    return this.axios.post(url).then(res => {
      let { meta, data } = res
      if (meta.status === 200) {
        this.setState({
          [dataName]: data.list
        })
      }
    })
  }
  // 页面加载完成,需要发送ajax请求,获取轮播图数据
  async componentDidMount() {
    // Promise.all()
    await Promise.all([
      this.doRequest('homes/swipe', 'imgList'),
      this.doRequest('homes/menu', 'menuList'),
      this.doRequest('homes/info', 'infoList'),
      this.doRequest('homes/faq', 'faqList'),
      this.doRequest('homes/house', 'houseList')
    ])
    this.setState({
      loading: false
    })
  }
  render() {
    return (
      <div className="main">
        <div className="search">
          <Input
            fluid
            icon={{ name: 'search', circular: true, link: true }}
            placeholder="搜房源..."
          />
        </div>
        <div className="content">
          <Dimmer inverted active={this.state.loading} page>
            <Loader>Loading</Loader>
          </Dimmer>
          {/* 轮播图 */}
          <ImageGallery
            items={this.state.imgList}
            showThumbnails={false}
            showFullscreenButton={false}
            showPlayButton={false}
            showBullets={true}
          />
          {/* 菜单部分 */}
          <MenuList data={this.state.menuList} />
          {/* 好客咨询部分 */}
          <InfoList data={this.state.infoList} />
          {/* 好客问答部分 */}
          <FaqList data={this.state.faqList} />
          {/* 房屋信息部分 */}
          <HouseList data={this.state.houseList} />
          {/* <div style={{ height: 1000 }} /> */}
        </div>
      </div>
    )
  }
}

export default Main

// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
  return (
    <Grid className="menu" divided padded>
      <Grid.Row columns={4}>
        {data.map(item => (
          <Grid.Column key={item.id}>
            <div className="home-menu-item">
              <Icon name="home" size="big" />
            </div>
            <div>{item.menu_name}</div>
          </Grid.Column>
        ))}
      </Grid.Row>
    </Grid>
  )
}

// 好客资讯组件
function InfoList({ data }) {
  return (
    <div className="home-msg">
      <Item.Group unstackable>
        <Item className="home-msg-img">
          <Item.Image
            size="tiny"
            src={'http://47.96.21.88:8086/public/zixun.png'}
          />
          <Item.Content verticalAlign="top">
            {data.map(item => (
              <Item.Header key={item.id}>
                <span>限购 ●</span>
                <span>{item.info_title}</span>
              </Item.Header>
            ))}
            <div className="home-msg-more">
              <Icon name="angle right" size="big" />
            </div>
          </Item.Content>
        </Item>
      </Item.Group>
    </div>
  )
}

// 好客问答组件
function FaqList({ data }) {
  return (
    <div className="home-ask">
      <div className="home-ask-title">好客问答</div>
      <ul>
        {data.map(item => (
          <li key={item.question_id}>
            <div>
              <Icon color="green" name="question circle outline" />
              <span>{item.question_name}</span>
            </div>
            <div>
              {item.question_tag.split(',').map(tag => (
                <Button key={tag} basic color="green" size="mini">
                  {tag}
                </Button>
              ))}
              <div>
                {item.atime} ● <Icon name="comment alternate outline" />{' '}
                {item.qnum}
              </div>
            </div>
          </li>
        ))}
      </ul>
    </div>
  )
}

// 房屋组件
function HouseList({ data }) {
  let newHouse = []
  let oldHouse = []
  let hireHouse = []
  data.forEach(item => {
    let temp = (
      <Item key={item.id}>
        <Item.Image src="http://47.96.21.88:8086/public/home.png" />
        <Item.Content>
          <Item.Header>{item.home_name}</Item.Header>
          <Item.Meta>
            <span className="cinema">{item.home_desc}</span>
          </Item.Meta>
          <Item.Description>
            {item.home_tags.split(',').map(tag => (
              <Button key={tag} basic color="green" size="mini">
                {tag}
              </Button>
            ))}
          </Item.Description>
          <Item.Description>{item.home_price}</Item.Description>
        </Item.Content>
      </Item>
    )
    if (item.home_type === 1) {
      newHouse.push(temp)
    } else if (item.home_type === 2) {
      oldHouse.push(temp)
    } else {
      hireHouse.push(temp)
    }
  })
  // console.log(data)
  return (
    <div>
      <div>
        <div className="home-hire-title">最新开盘</div>
        <Item.Group divided unstackable>
          {newHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">二手精选</div>
        <Item.Group divided unstackable>
          {oldHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">组一个家</div>
        <Item.Group divided unstackable>
          {hireHouse}
        </Item.Group>
      </div>
    </div>
  )
}

(5)my

--My.css

.my-container {
  overflow-y: auto;
  height: 100%;
}
.my-title {
  min-height: 300px;
  position: relative;
}
.my-title img {
  width: 100%;
}
.my-title .info {
  position: absolute;
  background: #fff;
  width: 60%;
  height: 65%;
  bottom: 0px;
  left: 50%;
  transform: translateX(-50%);
  box-shadow: 2px 2px 3px #787d82;
  margin: 50px auto 0;
  padding: 0 20px;
  padding-top: 0;
  text-align: center;
}
.my-title .myicon {
  position: relative;
  transform: translateY(-50%);
  border-radius: 50%;
  max-width: 90px;
  max-height: 90px;
  border: solid 5px #f5f5f5;
  display: inline-block;
  box-shadow: 0px 2px 2px #bdbdbd;
}
.my-title .myicon img {
  border-radius: 50%;
}
.my-title .name {
  margin-top: -30px;
  margin-bottom: 10px;
}
.my-title .edit {
  color: #787d82;
  margin-top: 20px;
}
.my-menu.ui.grid > .row > .column {
  margin-top: 20px;
  margin-bottom: 10px;
  color: #808589;
}
.my-menu.ui.grid > .row > .column div {
  margin-top: 5px;
}
.my-ad {
  text-align: center;
  margin-top: 10px;
}
.my-ad img {
  width: 85%;
}
.my-container::-webkit-scrollbar {
  width: 3px;
}
.my-container::-webkit-scrollbar-thumb {
  background-color: #cbdaea;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  min-height: 2rem;
  background-clip: padding-box;
  border-radius: 5px;
}
.my-container::-webkit-scrollbar-track {
  background-color: #fff;
}
.my-container::-webkit-scrollbar-track-piece {
  height: 30px;
}
.ui.modal > .actions {
  text-align: center;
}
.ui.modal > .content {
  text-align: center;
}
.ui.modal > .content .avatar-zoom {
  color: #000;
  margin-right: 10px;
  display: inline-block;
  vertical-align: text-bottom;
  padding-bottom: 2px;
  font-size: 18px;
}

--My.jsx

import React from 'react'
import './My.css'
import { Button, Grid, Icon, Modal } from 'semantic-ui-react'
import AvatarEditor from 'react-avatar-editor'
class My extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      avatar: '',
      username: '',
      isShowSelect: false, // 是否显示选择图片的模态框
      isShowCrop: false, // 是否显示裁切图片的模态框
      avatarFile: ''
    }
  }
  // 显示选择图片的弹窗
  showSelect = () => {
    this.setState({
      isShowSelect: true
    })
  }

  // 显示裁切图片的弹窗
  showCrop = file => {
    // 1. 隐藏选择图片的弹窗
    // 2. 把上传到的图片保存起来
    //3. 显示裁切图片的弹窗
    this.setState({
      isShowSelect: false,
      avatarFile: file,
      isShowCrop: true
    })
  }

  // 关闭裁切模态框
  closeCrop = avatar => {
    this.setState({
      isShowCrop: false,
      avatar: avatar
    })
  }

  // 当组件挂载好了,发送ajax请求,获取到个人信息
  // 接口需要参数: user_id
  async componentDidMount() {
    let res = await this.axios.post('my/info', {
      user_id: localStorage.getItem('uid')
    })
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        avatar: data.avatar,
        username: data.username
      })
    }
  }

  render() {
    return (
      <div className="my-container">
        <SelectAvatar open={this.state.isShowSelect} showCrop={this.showCrop} />
        <CropAvatar
          open={this.state.isShowCrop}
          avatarFile={this.state.avatarFile}
          closeCrop={this.closeCrop}
        />
        <div className="my-title">
          <img src={'http://127.0.0.1:9999/public/my-bg.png'} alt="me" />
          <div className="info">
            <div className="myicon">
              {/* 给头像注册点击事件,显示选择头像的弹窗 */}
              <img
                src={this.state.avatar}
                alt="icon"
                onClick={this.showSelect}
              />
            </div>
            <div className="name">{this.state.username}</div>
            <Button color="green" size="mini">
              已认证
            </Button>
            <div className="edit">编辑个人资料</div>
          </div>
        </div>
        <Grid padded className="my-menu">
          <Grid.Row columns={3}>
            <Grid.Column>
              <Icon name="clock outline" size="big" />
              <div>看房记录</div>
            </Grid.Column>
            <Grid.Column>
              <Icon name="yen sign" size="big" />
              <div>我的订单</div>
            </Grid.Column>
            <Grid.Column>
              <Icon name="bookmark outline" size="big" />
              <div>我的收藏</div>
            </Grid.Column>
            <Grid.Column>
              <Icon name="user outline" size="big" />
              <div>个人资料</div>
            </Grid.Column>
            <Grid.Column>
              <Icon name="home" size="big" />
              <div>身份认证</div>
            </Grid.Column>
            <Grid.Column>
              <Icon name="microphone" size="big" />
              <div>联系我们</div>
            </Grid.Column>
          </Grid.Row>
        </Grid>
        <div className="my-ad">
          <img src={'http://127.0.0.1:9999/public/ad.png'} alt="" />
        </div>
      </div>
    )
  }
}
export default My

// 选择头像的弹窗组件
class SelectAvatar extends React.Component {
  constructor(props) {
    super(props)
    this.fileRef = React.createRef()
  }

  submitFile = () => {
    // 获取到上传的文件, 传递给父组件
    let file = this.fileRef.current.files[0]
    this.props.showCrop(file)
  }

  render() {
    let { open } = this.props
    return (
      <div>
        {/* 弹窗组件 */}
        <Modal size="small" open={open}>
          <Modal.Header>选择图片</Modal.Header>
          <Modal.Content>
            <input type="file" ref={this.fileRef} />
          </Modal.Content>
          <Modal.Actions>
            <Button
              positive
              icon="checkmark"
              labelPosition="right"
              content="确定"
              onClick={this.submitFile}
            />
          </Modal.Actions>
        </Modal>
      </div>
    )
  }
}

// 裁切头像的弹窗组件
class CropAvatar extends React.Component {
  constructor() {
    super()
    this.state = {
      scale: 1
    }
  }

  handleScale = e => {
    this.setState({
      scale: e.target.value - 0
    })
  }

  // 设置编辑的ref
  setEditorRef = editor => (this.editor = editor)

  submitAvatar = async () => {
    // 发送ajax请求,上传裁切的图片
    //1. 如果获取到裁切后的图片
    let avatar = this.editor.getImageScaledToCanvas().toDataURL()
    // 2.发送ajax请求,修改图片了
    let res = await this.axios.post('my/avatar', {
      avatar: avatar
    })
    let { meta } = res
    if (meta.status === 200) {
      // 修改关闭模态框, 把裁切后的图片传给父组件,让父组件把图片修改
      this.props.closeCrop(avatar)
    }
  }
  render() {
    let { open, avatarFile } = this.props
    return (
      <div>
        <Modal size="small" open={open}>
          <Modal.Header>上传头像</Modal.Header>
          <Modal.Content>
            <AvatarEditor
              ref={this.setEditorRef}
              borderRadius={100}
              image={avatarFile}
              width={200}
              height={200}
              border={50}
              color={[255, 255, 255, 0.6]} // RGBA
              scale={this.state.scale}
              rotate={0}
            />
            <div>
              <span className="avatar-zoom">缩放:</span>
              <input
                value={this.state.scale}
                onChange={this.handleScale}
                name="scale"
                type="range"
                min="1"
                max="2"
                step="0.01"
              />
            </div>
          </Modal.Content>
          <Modal.Actions>
            <Button
              positive
              icon="checkmark"
              labelPosition="right"
              content="确定"
              onClick={this.submitAvatar}
            />
          </Modal.Actions>
        </Modal>
      </div>
    )
  }
}

2.2、Chat.jsx

import React from 'react'
class Chat extends React.Component {
  render() {
    return <div>这是Chat组件</div>
  }
}
export default Chat

2.3、Info.jsx

import React from 'react'
class Info extends React.Component {
  render() {
    return <div>这是Info组件</div>
  }
}
export default Info

2.4、Main.css

.main {
  width: 100%;
  height: 100%;
  position: relative;
}
.search {
  height: 40px;
  line-height: 40px;
  position: absolute;
  width: 100%;
  top: 0;
}

.content {
  width: 100%;
  height: 100%;
  padding-top: 40px;
  /* 给content添加了垂直的滚动条 */
  overflow-y: auto;
}

/* 滚动条样式 */
.content::-webkit-scrollbar {
  width: 3px;
}
.content::-webkit-scrollbar-thumb {
  background-color: #cbdaea;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  min-height: 2rem;
  background-clip: padding-box;
  border-radius: 5px;
}
.content::-webkit-scrollbar-track {
  background-color: #fff;
}

/* 菜单的样式 */
.home-menu-item {
  border-radius: 31px;
  height: 62px;
  line-height: 62px;
  color: #fff;
}

.ui.grid > .row > .column:nth-child(1) .home-menu-item {
  background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
  background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
  background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
  background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
  background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
  background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
  background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
  background-color: #7ccc39;
}

.ui.menu > .row > .column {
  text-align: center;
  margin-top: 10px;
}

/* 好客咨询样式 */
.home-msg {
  margin: 25px 0 15px 0;
  position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
  width: 50px !important;
  height: 50px !important;
  margin-left: 7px;
  margin-top: 2px;
}
.ui.items > .item > .content > .header {
  display: block;
  height: 25px;
  margin-top: 5px;
  font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
  color: orange;
  margin-right: 10px;
  overflow: hidden;
  display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: baseline;
  width: 65%;
}
.home-msg .home-msg-more {
  position: absolute;
  right: 5px;
  top: 12px;
}

/* 好客问答样式 */
.home-ask-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.home-ask ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.home-ask ul li {
  height: 70px;
  line-height: 70px;
}

.home-ask ul li div {
  height: 35px;
  line-height: 35px;
}

.home-ask ul li div:first-child {
  margin-left: 10px;
  font-size: 18px;
}
.home-ask ul li div:last-child {
  margin-left: 30px;
  position: relative;
}

.home-ask ul li div:last-child div {
  position: absolute;
  font-size: 14px;
  right: 15px;
  top: 0;
}

/* 房屋样式 */
.home-hire-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.ui.items > .item > .image {
  margin-left: 10px;
  margin-top: 6px;
}

.ui.items > .item > .content > .description:last-child {
  color: orange;
}

2.5、Main.jsx

import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
  Input,
  Grid,
  Icon,
  Item,
  Button,
  Dimmer,
  Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      imgList: [],
      menuList: [],
      infoList: [],
      faqList: [],
      houseList: [],
      loading: true
    }
  }

  /* // 获取轮播图数据
  getImgList = async () => {
    let res = await this.axios.post('homes/swipe')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        imgList: data.list
      })
    }
  }
  // 获取菜单数据
  getMenuList = async () => {
    let res = await this.axios.post('homes/menu')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        menuList: data.list
      })
    }
  }
  // 获取咨询数据
  getInfoList = async () => {
    let res = await this.axios.post('homes/info')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        infoList: data.list
      })
    }
  }
  // 获取咨询数据
  getFaqList = async () => {
    let res = await this.axios.post('homes/faq')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        faqList: data.list
      })
    }
  }
  // 获取房屋数据
  getHouseList = async () => {
    let res = await this.axios.post('homes/house')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        houseList: data.list
      })
    }
  } */
  doRequest = (url, dataName) => {
    return this.axios.post(url).then(res => {
      let { meta, data } = res
      if (meta.status === 200) {
        this.setState({
          [dataName]: data.list
        })
      }
    })
  }
  // 页面加载完成,需要发送ajax请求,获取轮播图数据
  async componentDidMount() {
    // Promise.all()
    await Promise.all([
      this.doRequest('homes/swipe', 'imgList'),
      this.doRequest('homes/menu', 'menuList'),
      this.doRequest('homes/info', 'infoList'),
      this.doRequest('homes/faq', 'faqList'),
      this.doRequest('homes/house', 'houseList')
    ])
    this.setState({
      loading: false
    })
  }
  render() {
    return (
      <div className="main">
        <div className="search">
          <Input
            fluid
            icon={{ name: 'search', circular: true, link: true }}
            placeholder="搜房源..."
          />
        </div>
        <div className="content">
          <Dimmer inverted active={this.state.loading} page>
            <Loader>Loading</Loader>
          </Dimmer>
          {/* 轮播图 */}
          <ImageGallery
            items={this.state.imgList}
            showThumbnails={false}
            showFullscreenButton={false}
            showPlayButton={false}
            showBullets={true}
          />
          {/* 菜单部分 */}
          <MenuList data={this.state.menuList} />
          {/* 好客咨询部分 */}
          <InfoList data={this.state.infoList} />
          {/* 好客问答部分 */}
          <FaqList data={this.state.faqList} />
          {/* 房屋信息部分 */}
          <HouseList data={this.state.houseList} />
          {/* <div style={{ height: 1000 }} /> */}
        </div>
      </div>
    )
  }
}

export default Main

// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
  return (
    <Grid className="menu" divided padded>
      <Grid.Row columns={4}>
        {data.map(item => (
          <Grid.Column key={item.id}>
            <div className="home-menu-item">
              <Icon name="home" size="big" />
            </div>
            <div>{item.menu_name}</div>
          </Grid.Column>
        ))}
      </Grid.Row>
    </Grid>
  )
}

// 好客资讯组件
function InfoList({ data }) {
  return (
    <div className="home-msg">
      <Item.Group unstackable>
        <Item className="home-msg-img">
          <Item.Image
            size="tiny"
            src={'http://47.96.21.88:8086/public/zixun.png'}
          />
          <Item.Content verticalAlign="top">
            {data.map(item => (
              <Item.Header key={item.id}>
                <span>限购 ●</span>
                <span>{item.info_title}</span>
              </Item.Header>
            ))}
            <div className="home-msg-more">
              <Icon name="angle right" size="big" />
            </div>
          </Item.Content>
        </Item>
      </Item.Group>
    </div>
  )
}

// 好客问答组件
function FaqList({ data }) {
  return (
    <div className="home-ask">
      <div className="home-ask-title">好客问答</div>
      <ul>
        {data.map(item => (
          <li key={item.question_id}>
            <div>
              <Icon color="green" name="question circle outline" />
              <span>{item.question_name}</span>
            </div>
            <div>
              {item.question_tag.split(',').map(tag => (
                <Button key={tag} basic color="green" size="mini">
                  {tag}
                </Button>
              ))}
              <div>
                {item.atime} ● <Icon name="comment alternate outline" />{' '}
                {item.qnum}
              </div>
            </div>
          </li>
        ))}
      </ul>
    </div>
  )
}

// 房屋组件
function HouseList({ data }) {
  let newHouse = []
  let oldHouse = []
  let hireHouse = []
  data.forEach(item => {
    let temp = (
      <Item key={item.id}>
        <Item.Image src="http://47.96.21.88:8086/public/home.png" />
        <Item.Content>
          <Item.Header>{item.home_name}</Item.Header>
          <Item.Meta>
            <span className="cinema">{item.home_desc}</span>
          </Item.Meta>
          <Item.Description>
            {item.home_tags.split(',').map(tag => (
              <Button key={tag} basic color="green" size="mini">
                {tag}
              </Button>
            ))}
          </Item.Description>
          <Item.Description>{item.home_price}</Item.Description>
        </Item.Content>
      </Item>
    )
    if (item.home_type === 1) {
      newHouse.push(temp)
    } else if (item.home_type === 2) {
      oldHouse.push(temp)
    } else {
      hireHouse.push(temp)
    }
  })
  // console.log(data)
  return (
    <div>
      <div>
        <div className="home-hire-title">最新开盘</div>
        <Item.Group divided unstackable>
          {newHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">二手精选</div>
        <Item.Group divided unstackable>
          {oldHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">组一个家</div>
        <Item.Group divided unstackable>
          {hireHouse}
        </Item.Group>
      </div>
    </div>
  )
}

2.6、My.jsx

import React from 'react'
class My extends React.Component {
  render() {
    return <div>这是My组件</div>
  }
}
export default My

2.7、Chat.jsx

import React from 'react'
class Chat extends React.Component {
  render() {
    return <div>这是Chat组件</div>
  }
}
export default Chat

2.8、Info.jsx

import React from 'react'
class Info extends React.Component {
  render() {
    return <div>这是Info组件</div>
  }
}
export default Info

2.9、Main.css

.main {
  width: 100%;
  height: 100%;
  position: relative;
}
.search {
  height: 40px;
  line-height: 40px;
  position: absolute;
  width: 100%;
  top: 0;
}

.content {
  width: 100%;
  height: 100%;
  padding-top: 40px;
  /* 给content添加了垂直的滚动条 */
  overflow-y: auto;
}

/* 滚动条样式 */
.content::-webkit-scrollbar {
  width: 3px;
}
.content::-webkit-scrollbar-thumb {
  background-color: #cbdaea;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  min-height: 2rem;
  background-clip: padding-box;
  border-radius: 5px;
}
.content::-webkit-scrollbar-track {
  background-color: #fff;
}

/* 菜单的样式 */
.home-menu-item {
  border-radius: 31px;
  height: 62px;
  line-height: 62px;
  color: #fff;
}

.ui.grid > .row > .column:nth-child(1) .home-menu-item {
  background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
  background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
  background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
  background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
  background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
  background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
  background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
  background-color: #7ccc39;
}

.ui.menu > .row > .column {
  text-align: center;
  margin-top: 10px;
}

/* 好客咨询样式 */
.home-msg {
  margin: 25px 0 15px 0;
  position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
  width: 50px !important;
  height: 50px !important;
  margin-left: 7px;
  margin-top: 2px;
}
.ui.items > .item > .content > .header {
  display: block;
  height: 25px;
  margin-top: 5px;
  font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
  color: orange;
  margin-right: 10px;
  overflow: hidden;
  display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: baseline;
  width: 65%;
}
.home-msg .home-msg-more {
  position: absolute;
  right: 5px;
  top: 12px;
}

/* 好客问答样式 */
.home-ask-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.home-ask ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.home-ask ul li {
  height: 70px;
  line-height: 70px;
}

.home-ask ul li div {
  height: 35px;
  line-height: 35px;
}

.home-ask ul li div:first-child {
  margin-left: 10px;
  font-size: 18px;
}
.home-ask ul li div:last-child {
  margin-left: 30px;
  position: relative;
}

.home-ask ul li div:last-child div {
  position: absolute;
  font-size: 14px;
  right: 15px;
  top: 0;
}

/* 房屋样式 */
.home-hire-title {
  height: 40px;
  font-weight: 600;
  font-size: 18px;
  line-height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #cecece;
}

.ui.items > .item > .image {
  margin-left: 10px;
  margin-top: 6px;
}

.ui.items > .item > .content > .description:last-child {
  color: orange;
}

2.10、Main.jsx

import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
  Input,
  Grid,
  Icon,
  Item,
  Button,
  Dimmer,
  Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      imgList: [],
      menuList: [],
      infoList: [],
      faqList: [],
      houseList: [],
      loading: true
    }
  }

  /* // 获取轮播图数据
  getImgList = async () => {
    let res = await this.axios.post('homes/swipe')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        imgList: data.list
      })
    }
  }
  // 获取菜单数据
  getMenuList = async () => {
    let res = await this.axios.post('homes/menu')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        menuList: data.list
      })
    }
  }
  // 获取咨询数据
  getInfoList = async () => {
    let res = await this.axios.post('homes/info')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        infoList: data.list
      })
    }
  }
  // 获取咨询数据
  getFaqList = async () => {
    let res = await this.axios.post('homes/faq')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        faqList: data.list
      })
    }
  }
  // 获取房屋数据
  getHouseList = async () => {
    let res = await this.axios.post('homes/house')
    let { meta, data } = res
    if (meta.status === 200) {
      this.setState({
        houseList: data.list
      })
    }
  } */
  doRequest = (url, dataName) => {
    return this.axios.post(url).then(res => {
      let { meta, data } = res
      if (meta.status === 200) {
        this.setState({
          [dataName]: data.list
        })
      }
    })
  }
  // 页面加载完成,需要发送ajax请求,获取轮播图数据
  async componentDidMount() {
    // Promise.all()
    await Promise.all([
      this.doRequest('homes/swipe', 'imgList'),
      this.doRequest('homes/menu', 'menuList'),
      this.doRequest('homes/info', 'infoList'),
      this.doRequest('homes/faq', 'faqList'),
      this.doRequest('homes/house', 'houseList')
    ])
    this.setState({
      loading: false
    })
  }
  render() {
    return (
      <div className="main">
        <div className="search">
          <Input
            fluid
            icon={{ name: 'search', circular: true, link: true }}
            placeholder="搜房源..."
          />
        </div>
        <div className="content">
          <Dimmer inverted active={this.state.loading} page>
            <Loader>Loading</Loader>
          </Dimmer>
          {/* 轮播图 */}
          <ImageGallery
            items={this.state.imgList}
            showThumbnails={false}
            showFullscreenButton={false}
            showPlayButton={false}
            showBullets={true}
          />
          {/* 菜单部分 */}
          <MenuList data={this.state.menuList} />
          {/* 好客咨询部分 */}
          <InfoList data={this.state.infoList} />
          {/* 好客问答部分 */}
          <FaqList data={this.state.faqList} />
          {/* 房屋信息部分 */}
          <HouseList data={this.state.houseList} />
          {/* <div style={{ height: 1000 }} /> */}
        </div>
      </div>
    )
  }
}

export default Main

// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
  return (
    <Grid className="menu" divided padded>
      <Grid.Row columns={4}>
        {data.map(item => (
          <Grid.Column key={item.id}>
            <div className="home-menu-item">
              <Icon name="home" size="big" />
            </div>
            <div>{item.menu_name}</div>
          </Grid.Column>
        ))}
      </Grid.Row>
    </Grid>
  )
}

// 好客资讯组件
function InfoList({ data }) {
  return (
    <div className="home-msg">
      <Item.Group unstackable>
        <Item className="home-msg-img">
          <Item.Image
            size="tiny"
            src={'http://47.96.21.88:8086/public/zixun.png'}
          />
          <Item.Content verticalAlign="top">
            {data.map(item => (
              <Item.Header key={item.id}>
                <span>限购 ●</span>
                <span>{item.info_title}</span>
              </Item.Header>
            ))}
            <div className="home-msg-more">
              <Icon name="angle right" size="big" />
            </div>
          </Item.Content>
        </Item>
      </Item.Group>
    </div>
  )
}

// 好客问答组件
function FaqList({ data }) {
  return (
    <div className="home-ask">
      <div className="home-ask-title">好客问答</div>
      <ul>
        {data.map(item => (
          <li key={item.question_id}>
            <div>
              <Icon color="green" name="question circle outline" />
              <span>{item.question_name}</span>
            </div>
            <div>
              {item.question_tag.split(',').map(tag => (
                <Button key={tag} basic color="green" size="mini">
                  {tag}
                </Button>
              ))}
              <div>
                {item.atime} ● <Icon name="comment alternate outline" />{' '}
                {item.qnum}
              </div>
            </div>
          </li>
        ))}
      </ul>
    </div>
  )
}

// 房屋组件
function HouseList({ data }) {
  let newHouse = []
  let oldHouse = []
  let hireHouse = []
  data.forEach(item => {
    let temp = (
      <Item key={item.id}>
        <Item.Image src="http://47.96.21.88:8086/public/home.png" />
        <Item.Content>
          <Item.Header>{item.home_name}</Item.Header>
          <Item.Meta>
            <span className="cinema">{item.home_desc}</span>
          </Item.Meta>
          <Item.Description>
            {item.home_tags.split(',').map(tag => (
              <Button key={tag} basic color="green" size="mini">
                {tag}
              </Button>
            ))}
          </Item.Description>
          <Item.Description>{item.home_price}</Item.Description>
        </Item.Content>
      </Item>
    )
    if (item.home_type === 1) {
      newHouse.push(temp)
    } else if (item.home_type === 2) {
      oldHouse.push(temp)
    } else {
      hireHouse.push(temp)
    }
  })
  // console.log(data)
  return (
    <div>
      <div>
        <div className="home-hire-title">最新开盘</div>
        <Item.Group divided unstackable>
          {newHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">二手精选</div>
        <Item.Group divided unstackable>
          {oldHouse}
        </Item.Group>
      </div>
      <div>
        <div className="home-hire-title">组一个家</div>
        <Item.Group divided unstackable>
          {hireHouse}
        </Item.Group>
      </div>
    </div>
  )
}

2.11、My.jsx

import React from 'react'
class My extends React.Component {
  render() {
    return <div>这是My组件</div>
  }
}
export default My

3、Home.css

.home {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}
.home_content {
  position: absolute;
  width: 100%;
  top: 0;
  bottom: 60px;
  /* background-color: lightgreen; */
}

.home_menu {
  position: absolute;
  width: 100%;
  height: 60px;
  bottom: 0;
  /* background-color: lightblue; */
}

.home_menu .ui.grid {
  margin: 0;
}

.home_menu .ui.grid > .row {
  padding: 0;
}

.home_menu .column {
  height: 60px;
  border-right: 1px solid #ccc;
  background-color: #eee;
  box-sizing: border-box;
}

.home_menu .column a {
  color: #666;
  width: 100%;
  height: 100%;
  display: block;
  text-align: center;
}

.home_menu .column a.active {
  color: darkorange;
}

.home_menu .column i {
  font-size: 24px;
  height: 40px;
  line-height: 40px;
}

.home_menu .column p {
  height: 20px;
  line-height: 20px;
}

4、Home.jsx

import React from 'react'
import './Home.css'
import { Grid, Icon } from 'semantic-ui-react'
import { NavLink, Switch, Route } from 'react-router-dom'
import Main from './home/main/Main'
import Info from './home/info/Info'
import Chat from './home/chat/Chat'
import My from './home/my/My'
// import Demo from './home/demo/Demo.jsx'
class Home extends React.Component {
  // 点标记语法
  render() {
    return (
      <div className="home">
        <div className="home_content">
          <Switch>
            <Route exact path="/home" component={Main} />
            <Route path="/home/info" component={Info} />
            <Route path="/home/my" component={My} />
            <Route path="/home/chat" component={Chat} />
          </Switch>
        </div>
        <div className="home_menu">
          {/* 网格布局 */}
          <Grid>
            <Grid.Row columns={4}>
              <Grid.Column>
                <NavLink exact to="/home">
                  <Icon name="hospital" />
                  <p>主页</p>
                </NavLink>
              </Grid.Column>
              <Grid.Column>
                <NavLink to="/home/info">
                  <Icon name="paste" />
                  <p>资讯</p>
                </NavLink>
              </Grid.Column>
              <Grid.Column>
                <NavLink to="/home/chat">
                  <Icon name="envelope" />
                  <p>微聊</p>
                </NavLink>
              </Grid.Column>
              <Grid.Column>
                <NavLink to="/home/my">
                  <Icon name="user" />
                  <p>我的</p>
                </NavLink>
              </Grid.Column>
            </Grid.Row>
          </Grid>
        </div>
      </div>
    )
  }
}

export default Home

5、Login.css

.login_container {
  height: 100%;
  background-color: #90ee90;
}

.login_title {
  height: 50px;
  line-height: 50px;
  background-color: #21b97a;
  text-align: center;
  color: #fff;
  font-size: 24px;
}

.login_form {
  margin: 20px 10px;
}

6、Login.jsx

import React from 'react'
// 引入semanticui的组件
import { Form } from 'semantic-ui-react'
// 引入Login.css样式
import './Login.css'

// 引入withRouter实现编程式导航
import { withRouter } from 'react-router-dom'

class Login extends React.Component {
  // 构造函数
  constructor(props) {
    super(props)
    this.state = {
      uname: '',
      pwd: ''
    }
  }

  // 用于处理受控组件
  handleChange = e => {
    let { name, value } = e.target
    this.setState({
      [name]: value
    })
  }

  // 登录功能
  login = async e => {
    e.preventDefault()
    let { history } = this.props
    // 发送请求
    let { uname, pwd } = this.state
    // await必须在async函数中使用,await可以在promise对象前面使用
    // await会暂停aysnc函数的执行,等待promise的结果,才会继续async函数的执行
    let res = await this.axios.post('users/login', {
      uname,
      pwd
    })
    let { meta, data } = res
    if (meta.status === 200) {
      //1. 把token给保存到浏览器本地
      localStorage.setItem('myToken', data.token)
      //2. 把userid存储起来
      localStorage.setItem('uid', data.uid)
      //3. 跳转到home组件
      history.push('/home')
    } else {
      console.log(meta.msg)
    }
  }

  // 组件的渲染方法
  render() {
    return (
      <div className="login_container">
        <div className="login_title">登录</div>
        <div className="login_form">
          {/* Form:表示整个表单组件 */}
          {/* Form.Field:表示表单的一个字段 */}
          <Form action="" onSubmit={this.login}>
            <Form.Field>
              <Form.Input
                size="big"
                icon="user"
                iconPosition="left"
                placeholder="请输入用户名..."
                name="uname"
                autoComplete="off"
                value={this.state.uname}
                onChange={this.handleChange}
                required
              />
            </Form.Field>
            <Form.Field>
              <Form.Input
                size="big"
                icon="lock"
                iconPosition="left"
                placeholder="请输入密码..."
                name="pwd"
                autoComplete="off"
                value={this.state.pwd}
                onChange={this.handleChange}
                required
              />
            </Form.Field>
            <Form.Field>
              <Form.Button fluid positive size="big">
                登录
              </Form.Button>
            </Form.Field>
          </Form>
        </div>
      </div>
    )
  }
}

export default withRouter(Login)

7、App.jsx

import React from 'react'
// 导入路由
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Redirect
} from 'react-router-dom'
// 导入组件
import Home from './components/Home'
import Login from './components/Login'

class App extends React.Component {
  render() {
    return (
      <Router>
        <Switch>
          <Redirect exact path="/" to="/login" />
          <Route path="/login" component={Login} />
          <Route path="/home" component={Home} />
        </Switch>
      </Router>
    )
  }
}
export default App

8、index.css

body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

#root {
  height: 100%;
  width: 100%;
}

9、index.js

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import 'semantic-ui-css/semantic.min.css'
import App from './App'

// 导入axios对象
import axios from 'axios'
// 把axios对象绑定到了React组件的原型上,将来所有的react组件都能访问到axios对象
React.Component.prototype.axios = axios

// 给axios对象配置默认全局路径
axios.defaults.baseURL = 'http://localhost:9999/'
// axios.defaults.baseURL = 'http://47.96.21.88:8086/'

// 给axios配置一个响应拦截器 直接把data中的数据返回
axios.interceptors.response.use(
  function(response) {
    // 拦截到axios所有的请求,直接返回了响应结果中的data数据
    return response.data
  },
  function(error) {
    return error
  }
)

// 配置请求拦截器,每次请求,除了login,都可以添加token值
axios.interceptors.request.use(
  function(config) {
    if (!window.location.href.endsWith('/login')) {
      config.headers.Authorization = localStorage.getItem('myToken')
    }
    return config
  },
  function(error) {
    return Promise.reject(error)
  }
)

ReactDOM.render(<App />, document.getElementById('root'))
发布了284 篇原创文章 · 获赞 45 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_31784189/article/details/103357449
今日推荐