React项目实战之租房app项目(六)渲染房源列表&axios优化&封装顶部搜索栏&列表找房模块之条件筛选

前言

一、地图找房模块-获取并渲染房源列表

1.1 房源列表示例图

在这里插入图片描述

1.2 实现步骤

1、在单击事件中,获取小区下的所有房源数据
2、展示房源列表
3、渲染获取到的房源列表
4、调用地图 panBy() 方法,移动地图到中间位置。
5、监听地图 movestart 事件,在地图移动时隐藏房源列表。

1.3 代码示例

在src/pages/Map/index.js中添加如下代码:

  // 创建小区覆盖物
  createRect(point, name, count, id) {
    
    
    // 创建覆盖物
    const label = new BMap.Label('', {
    
    
      position: point,
      offset: new BMap.Size(-50, -28)
    })

    // 给 label 对象添加一个唯一标识
    label.id = id

    // 设置房源覆盖物内容
    label.setContent(`
      <div class="${
      
      styles.rect}">
        <span class="${
      
      styles.housename}">${
      
      name}</span>
        <span class="${
      
      styles.housenum}">${
      
      count}套</span>
        <i class="${
      
      styles.arrow}"></i>
      </div>
    `)

    // 设置样式
    label.setStyle(labelStyle)

    // 添加单击事件
    label.addEventListener('click', e => {
    
    
      //调用获取房源列表方法
      this.getHousesList(id)

      // 获取当前被点击项
      const target = e.changedTouches[0]
      // 调用地图 panBy() 方法,移动地图到中间位置 
      this.map.panBy(
        window.innerWidth / 2 - target.clientX,
        (window.innerHeight - 330) / 2 - target.clientY
      )
    })

    // 添加覆盖物到地图中
    this.map.addOverlay(label)
  } 

获取小区房源数据方法:

  // 获取小区房源数据
  async getHousesList(id) {
    
    
    const res = await axios.get(`http://localhost:8080/houses?cityId=${
      
      id}`)
    this.setState({
    
    
      housesList: res.data.body.list,

      // 展示房源列表
      isShowList: true
    })
  }

渲染小区房源列表方法:

  // 封装渲染房屋列表的方法
  renderHousesList() {
    
    
    return this.state.housesList.map(item => (
      <div className={
    
    styles.house} key={
    
    item.houseCode}>
        <div className={
    
    styles.imgWrap}>
          <img
            className={
    
    styles.img}
            src={
    
    `http://localhost:8080${
      
      item.houseImg}`}
            alt=""
          />
        </div>
        <div className={
    
    styles.content}>
          <h3 className={
    
    styles.title}>{
    
    item.title}</h3>
          <div className={
    
    styles.desc}>{
    
    item.desc}</div>
          <div>
            {
    
    /* ['近地铁', '随时看房'] */}
            {
    
    item.tags.map((tag, index) => {
    
    
              const tagClass = 'tag' + (index + 1)
              return (
                <span
                  className={
    
    [styles.tag, styles[tagClass]].join(' ')}
                  key={
    
    tag}
                >
                  {
    
    tag}
                </span>
              )
            })}
          </div>
          <div className={
    
    styles.price}>
            <span className={
    
    styles.priceNum}>{
    
    item.price}</span>/</div>
        </div>
      </div>
    ))
  }

在render方法中渲染房源列表,通过isShowList控制显示与隐藏:

render() {
    
    
    return (
      <div className={
    
    styles.map}>
        {
    
    /* 顶部导航栏组件 */}
        <NavHeader>地图找房</NavHeader>
        {
    
    /* 地图容器元素 */}
        <div id="container" className={
    
    styles.container} />

        {
    
    /* 房源列表 */}
        {
    
    /* 添加 styles.show 展示房屋列表 */}
        <div
          className={
    
    [
            styles.houseList,
            this.state.isShowList ? styles.show : ''
          ].join(' ')}
        >
          <div className={
    
    styles.titleWrap}>
            <h1 className={
    
    styles.listTitle}>房屋列表</h1>
            <Link className={
    
    styles.titleMore} to="/home/list">
              更多房源
            </Link>
          </div>

          <div className={
    
    styles.houseItems}>
            {
    
    /* 房屋结构 */}
            {
    
    this.renderHousesList()}
          </div>
        </div>
      </div>
    )
  }

注意:房源列表相关样式在index.module.css中,这里不做过多展示

最后,在初始化地图方法中绑定地图移动事件隐藏房源列表:

// 给地图绑定移动事件
map.addEventListener('movestart', () => {
    
    
  if (this.state.isShowList) {
    
    
    this.setState({
    
    
      //隐藏房源列表
      isShowList: false
    })
  }
})

二、axios优化

2.1 问题概述

当每一次请求接口的时候,都需要写相同的baseUrl。例如:http://localhost:8080
这样太繁琐,而且当我们的项目部署上线时,很难替换

2.2 配置生产环境和开发环境

配置步骤:

1、在项目根目录中创建文件 .env.development
2、在该文件中添加环境变量
//环境变量约定REACT_APP开头
REACT_APP_URL =http://localhost:8080
3、重新启动脚手架,脚手架在运行的时候就会解析这个文件
4、在utils/url.js 中,创建BASE_URL 变量
5、导出BASE_URL

代码示例:
在src/utils/url.js中添加如下代码:

// 配置baseURL
export const BASE_URL = process.env.REACT_APP_URL

最后,可以在其他组件导入使用:

import {
    
    BASE_URL} from '../../utils/url.js'

2.3 axios优化

实现步骤:

1、在utils/api.js 中,导入 axios和BASE_URL
2、调用 axios.create() 方法创建一个axios实例
3、给 create 方法,添加配置baseURL,值为 BASE_URL
4、导出API对象

代码示例:
在src/utils/api.js中添加如下代码:

import axios from 'axios'
import {
    
     BASE_URL } from './url.js'

// 创建axios的配置文件,里面配置baseURL路径
const config = {
    
    
    baseURL: BASE_URL
}

// 根据create 方法来构建axios对象
const instance = axios.create(config)

export {
    
     instance }

三、封装顶部搜索导航栏

实现步骤:

1、在components 目录中创建组件 SearchHeader/index.js
2、把之前写过的结构拷贝到这个文件中
3、然后把跟首页相关的数据去掉,标题,城市名称
4、通过props来进行传递

代码示例:
在src/components/SearchHeader/index.js中添加如下代码:

import {
    
     Flex } from 'antd-mobile';
import React from 'react'
import {
    
    withRouter} from 'react-router-dom'
import './index.scss'
import PropTypes from 'prop-types'

function SearchHeader({
     
      history, cityName, className}) {
    
    
    return (
        <Flex className={
    
    ['search-box', className || ''].join(' ')}>
            {
    
    /* 左侧白色区域 */}
            <Flex className="search">
                {
    
    /* 位置 */}
                <div className="location" onClick={
    
    () => history.push('/citylist')}>
                    <span className="name">{
    
    cityName}</span>
                    <i className="iconfont icon-arrow" />
                </div>

                {
    
    /* 搜索表单 */}
                <div className="form" onClick={
    
    () => history.push('/search')}>
                    <i className="iconfont icon-seach" />
                    <span className="text">请输入小区或地址</span>
                </div>
            </Flex>
            {
    
    /* 右侧地图图标 */}
            <i className="iconfont icon-map" onClick={
    
    () => history.push('/map')} />
        </Flex>
    )
}
// 设置校验
SearchHeader.propTypes = {
    
    
    cityName: PropTypes.string.isRequired
}
export default withRouter(SearchHeader)

四、列表找房模块-导入顶部导航栏组件

实现步骤:

1、在找房页面 SearchHeader 组件基础上,调整结构(添加返回icon等)。
2、给 SearchHeader 组件传递 className 属性,来调整组件样式,让其适应找房页面效果。

代码示例:
然后在src/pages/Houselist/index.js中导入顶部搜索栏组件:

import React from 'react'
import {
    
     Flex } from 'antd-mobile'

// 导入搜索导航栏组件
import SearchHeader from '../../components/SearchHeader'

// 导入样式
import styles from './index.module.css'

// 获取当前定位城市信息
const {
    
     label } = JSON.parse(localStorage.getItem('hkzf_city'))

export default class HouseList extends React.Component {
    
    
  render() {
    
    
    return (
      <div>
        <Flex className={
    
    styles.header}>
          <i
            className="iconfont icon-back"
            onClick={
    
    () => this.props.history.go(-1)}
          />
          <SearchHeader cityName={
    
    label} className={
    
    styles.searchHeader} />
        </Flex>
      </div>
    )
  }
}

最后,在当前目录下创建index.module.css,设置相应的样式

五、列表找房模块-条件筛选(上)

5.1 条件筛选效果图

在这里插入图片描述

5.2 功能分析

条件筛选需要具备的功能如下:

1、点击标题菜单,展开条件筛选对话框,被点击的标题高亮
2、点击取消按钮或空白区域,隐藏对话框,取消标题高亮
3、选择筛选条件后,点击确定按钮,隐藏对话框,当前标题高亮
4、打开对话框时,如果有选择的条件,那么默认显示已选择的条件
5、打开对话框已经隐藏对话框有动画效果
6、标题框具有吸顶功能

要实现以上功能需要封装4个组件分工合作,分别是:

1、父组件:Filter
2、子组件:FilterTitle 标题菜单组件
3、子组件:FilterPicker 前三个菜单对应的内容组件
4、子组件:FilterMore 最后一个菜单对应的内容组件

5.3 标题菜单组件FilterTitle组件实现

实现思路:

1、根据标题菜单数据,渲染标题列表
2、标题可以被点击
3、标题点击时高亮,有筛选条件选中时高亮
4、由父组件Filter提供高亮状态,子组件通过props接受状态来实现高亮
5、原则是单一数据源,只由父组件Filter提供和修改状态,其他组件只负责接收状态

实现步骤:

1、在src/pages/Houselist目录下,新建components文件夹
2、在components目录下新建Filter,FilterTitle,FilterPicker,FilterMore组件
3、在FilterTitle组件中通过props接收高亮状态对象 titleSelectedStatus
4、遍历titleList数组,渲染标题列表
5、判断高亮对象中当前标题是否高亮,如果是,添加高亮类
6、给标题项绑定单击事件,在事件中调用父组件传过来的方法 onClick
7、将当前标题type,通过onClick的参数,传递给父组件
8、父组件中接收到当前type,修改标题的选中状态为true

代码示例:
首先,在src/pages/HouseList/components/Filter/index.js中添加如下代码:

// 标题高亮状态
// true 表示高亮; false 表示不高亮
const titleSelectedStatus = {
    
    
  area: false,
  mode: false,
  price: false,
  more: false
}

export default class Filter extends Component {
    
    
  state = {
    
    
    titleSelectedStatus
  }

  // 点击标题菜单实现高亮
  // 父组件中接收到当前type,修改标题的选中状态为true
  onTitleClick = type => {
    
    
    this.setState(prevState => {
    
    
      return {
    
    
        titleSelectedStatus: {
    
    
          // 获取当前对象中所有属性的值
          ...prevState.titleSelectedStatus,
          [type]: true
        }
      }
    })
  }

  render() {
    
    
    const {
    
     titleSelectedStatus } = this.state

    return (
      <div className={
    
    styles.root}>
        <div className={
    
    styles.content}>
          {
    
    /* 标题栏 */}
          <FilterTitle
            titleSelectedStatus={
    
    titleSelectedStatus}
            onClick={
    
    this.onTitleClick}
          />
        </div>
      </div>
    )
  }
}

然后,在src/pages/HouseList/components/FilterTitle/index.js中添加如下代码:

// 条件筛选栏标题数组:
const titleList = [
  {
    
     title: '区域', type: 'area' },
  {
    
     title: '方式', type: 'mode' },
  {
    
     title: '租金', type: 'price' },
  {
    
     title: '筛选', type: 'more' }
]

export default function FilterTitle({
     
      titleSelectedStatus, onClick }) {
    
    
  return (
    <Flex align="center" className={
    
    styles.root}>
      {
    
    titleList.map(item => {
    
    
        const isSelected = titleSelectedStatus[item.type]
        return (
          <Flex.Item key={
    
    item.type} onClick={
    
    () => onClick(item.type)}>
            {
    
    /* 选中类名: selected */}
            <span
              className={
    
    [
                styles.dropdown,
                // 判断当前的标题是否是选中状态,如果是,设置选中的样式
                isSelected ? styles.selected : ''
              ].join(' ')}
            >
              <span>{
    
    item.title}</span>
              <i className="iconfont icon-arrow" />
            </span>
          </Flex.Item>
        )
      })}
    </Flex>
  )
}

总结

由于篇幅有限,条件筛选其他组件将留到下一篇笔记介绍

猜你喜欢

转载自blog.csdn.net/qq_40652101/article/details/128110581
今日推荐