1、在单击事件中,获取小区下的所有房源数据
2、展示房源列表
3、渲染获取到的房源列表
4、调用地图 panBy() 方法,移动地图到中间位置。
5、监听地图 movestart 事件,在地图移动时隐藏房源列表。
在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(`
${styles.rect}">
${styles.housename}">${name}
${styles.housenum}">${count}套
${styles.arrow}">
`)
// 设置样式
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
})
}
})
当每一次请求接口的时候,都需要写相同的baseUrl。例如:http://localhost:8080
这样太繁琐,而且当我们的项目部署上线时,很难替换
配置步骤:
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'
实现步骤:
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,设置相应的样式
条件筛选需要具备的功能如下:
1、点击标题菜单,展开条件筛选对话框,被点击的标题高亮
2、点击取消按钮或空白区域,隐藏对话框,取消标题高亮
3、选择筛选条件后,点击确定按钮,隐藏对话框,当前标题高亮
4、打开对话框时,如果有选择的条件,那么默认显示已选择的条件
5、打开对话框已经隐藏对话框有动画效果
6、标题框具有吸顶功能
要实现以上功能需要封装4个组件分工合作,分别是:
1、父组件:Filter
2、子组件:FilterTitle 标题菜单组件
3、子组件:FilterPicker 前三个菜单对应的内容组件
4、子组件:FilterMore 最后一个菜单对应的内容组件
实现思路:
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>
)
}
由于篇幅有限,条件筛选其他组件将留到下一篇笔记介绍