import React from 'react'
// 导入axios
import axios from 'axios'
import { Link } from 'react-router-dom'
// 导入封装好的 NavHeader 组件
import NavHeader from '../../components/NavHeader'
// 导入样式
// import './index.scss'
import styles from './index.module.css'
// 解决脚手架中全局变量访问的问题
const BMapGL = window.BMapGL
// 覆盖物样式
const labelStyle = {
cursor: 'pointer',
border: '0px solid rgb(255, 0, 0)',
padding: '0px',
whiteSpace: 'nowrap',
fontSize: '12px',
color: 'rgb(255, 255, 255)',
textAlign: 'center'
}
export default class Map extends React.Component {
state = {
// 小区下的房源列表
housesList: [],
// 表示是否展示房源列表
isShowList: false
}
componentDidMount() {
this.initMap()
}
// 初始化地图
initMap() {
// 获取当前定位城市
const { label, value } = JSON.parse(localStorage.getItem('hkzf_city'))
// 初始化地图实例
const map = new BMapGL.Map('container')
// 作用:能够在其他方法中通过 this 来获取到地图对象
this.map = map
// 创建地址解析器实例
const myGeo = new BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
myGeo.getPoint(
label,
async point => {
if (point) {
// 初始化地图
map.centerAndZoom(point, 11)
// 添加常用控件
map.addControl(new BMapGL.NavigationControl())
map.addControl(new BMapGL.ScaleControl())
// 调用 renderOverlays 方法
this.renderOverlays(value)
}
},
label
)
// 给地图绑定移动事件
map.addEventListener('movestart', () => {
// console.log('movestart')
if (this.state.isShowList) {
this.setState({
isShowList: false
})
}
})
}
// 渲染覆盖物入口
// 1 接收区域 id 参数,获取该区域下的房源数据
// 2 获取房源类型以及下级地图缩放级别
async renderOverlays(id) {
const res = await axios.get(`http://localhost:8080/area/map?id=${id}`)
// console.log('renderOverlays 获取到的数据:', res)
const data = res.data.body
// 调用 getTypeAndZoom 方法获取级别和类型
const { nextZoom, type } = this.getTypeAndZoom()
data.forEach(item => {
// 创建覆盖物
this.createOverlays(item, nextZoom, type)
})
}
// 计算要绘制的覆盖物类型和下一个缩放级别
// 区 -> 11 ,范围:>=10 <12
// 镇 -> 13 ,范围:>=12 <14
// 小区 -> 15 ,范围:>=14 <16
getTypeAndZoom() {
// 调用地图的 getZoom() 方法,来获取当前缩放级别
const zoom = this.map.getZoom()
let nextZoom, type
// console.log('当前地图缩放级别:', zoom)
if (zoom >= 10 && zoom < 12) {
// 区
// 下一个缩放级别
nextZoom = 13
// circle 表示绘制圆形覆盖物(区、镇)
type = 'circle'
} else if (zoom >= 12 && zoom < 14) {
// 镇
nextZoom = 15
type = 'circle'
} else if (zoom >= 14 && zoom < 16) {
// 小区
type = 'rect'
}
return {
nextZoom,
type
}
}
// 创建覆盖物
createOverlays(data, zoom, type) {
const {
coord: { longitude, latitude },
label: areaName,
count,
value
} = data
// 创建坐标对象
const areaPoint = new BMapGL.Point(longitude, latitude)
if (type === 'circle') {
// 区或镇
this.createCircle(areaPoint, areaName, count, value, zoom)
} else {
// 小区
this.createRect(areaPoint, areaName, count, value)
}
}
// 创建区、镇覆盖物
createCircle(point, name, count, id, zoom) {
// 创建覆盖物
const label = new BMapGL.Label('', {
position: point,
offset: new BMapGL.Size(-35, -35)
})
// 给 label 对象添加一个唯一标识
label.id = id
// 设置房源覆盖物内容
label.setContent(`
<div class="${styles.bubble}">
<p class="${styles.name}">${name}</p>
<p>${count}套</p>
</div>
`)
// 设置样式
label.setStyle(labelStyle)
// 添加单击事件
label.addEventListener('click', () => {
// 调用 renderOverlays 方法,获取该区域下的房源数据
this.renderOverlays(id)
// 放大地图,以当前点击的覆盖物为中心放大地图
this.map.centerAndZoom(point, zoom)
// 解决清除覆盖物时,百度地图API的JS文件自身报错的问题
setTimeout(() => {
// 清除当前覆盖物信息
this.map.clearOverlays()
}, 0)
})
// 添加覆盖物到地图中
this.map.addOverlay(label)
}
// 创建小区覆盖物
createRect(point, name, count, id) {
// 创建覆盖物
const label = new BMapGL.Label('', {
position: point,
offset: new BMapGL.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 => {
/*
1 创建 Label 、设置样式、设置 HTML 内容,绑定单击事件。
2 在单击事件中,获取该小区的房源数据。
3 展示房源列表。
4 渲染获取到的房源数据。
5 调用地图 panBy() 方法,移动地图到中间位置。
公式:
垂直位移:(window.innerHeight - 330) / 2 - target.clientY
水平平移:window.innerWidth / 2 - target.clientX
6 监听地图 movestart 事件,在地图移动时隐藏房源列表。
*/
this.getHousesList(id)
// 获取当前被点击项
const target = e.changedTouches[0]
this.map.panBy(
window.innerWidth / 2 - target.clientX,
(window.innerHeight - 330) / 2 - target.clientY
)
// console.log('小区被点击了')
})
// 添加覆盖物到地图中
this.map.addOverlay(label)
}
// 获取小区房源数据
async getHousesList(id) {
const res = await axios.get(`http://localhost:8080/houses?cityId=${id}`)
// console.log('小区的房源数据:', res)
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() {
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>
)
}
}