import React from 'react'
// 导入axios
import axios from 'axios'
import { Link } from 'react-router-dom'
import { Toast } from 'antd-mobile'
// 导入封装好的 NavHeader 组件
import NavHeader from '../../components/NavHeader'
// 导入样式
// import './index.scss'
import styles from './index.module.css'
// 解决脚手架中全局变量访问的问题
const BMap = window.BMap
// 覆盖物样式
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 BMap.Map('container')
// 作用:能够在其他方法中通过 this 来获取到地图对象
this.map = map
// 创建地址解析器实例
const myGeo = new BMap.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
myGeo.getPoint(
label,
async point => {
if (point) {
// 初始化地图
map.centerAndZoom(point, 11)
// 添加常用控件
map.addControl(new BMap.NavigationControl())
map.addControl(new BMap.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) {
try {
// 开启loading
Toast.loading('加载中...', 0, null, false)
const res = await axios.get(`http://localhost:8080/area/map?id=${id}`)
// 关闭 loading
Toast.hide()
const data = res.data.body
// 调用 getTypeAndZoom 方法获取级别和类型
const { nextZoom, type } = this.getTypeAndZoom()
data.forEach(item => {
// 创建覆盖物
this.createOverlays(item, nextZoom, type)
})
} catch (e) {
// 关闭 loading
Toast.hide()
}
}
// 计算要绘制的覆盖物类型和下一个缩放级别
// 区 -> 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 BMap.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 BMap.Label('', {
position: point,
offset: new BMap.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 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]
this.map.panBy(
window.innerWidth / 2 - target.clientX,
(window.innerHeight - 330) / 2 - target.clientY
)
})
// 添加覆盖物到地图中
this.map.addOverlay(label)
}
// 获取小区房源数据
async getHousesList(id) {
try {
// 开启loading
Toast.loading('加载中...', 0, null, false)
const res = await axios.get(`http://localhost:8080/houses?cityId=${id}`)
// 关闭 loading
Toast.hide()
this.setState({
housesList: res.data.body.list,
// 展示房源列表
isShowList: true
})
} catch (e) {
// 关闭 loading
Toast.hide()
}
}
// 封装渲染房屋列表的方法
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>
)
}
}
样式:
.map {
height: 100%;
padding-top: 45px;
}
.container {
height: 100%;
}
.map :global(.am-navbar) {
margin-top: -45px;
}
/* 区、镇的覆盖物样式: */
.bubble {
width: 70px;
height: 70px;
line-height: 0;
display: inline-block;
position: absolute;
border-radius: 100%;
background: rgba(12, 181, 106, 0.9);
color: #fff;
border: 2px solid rgba(255, 255, 255, 0.8);
text-align: center;
cursor: pointer;
}
.name {
padding: 18px 0 6px 0;
}
/* 小区的覆盖物样式: */
.rect {
height: 20px;
line-height: 19px;
width: 100px;
padding: 0 3px;
border-radius: 3px;
position: absolute;
background: rgba(12, 181, 106, 0.9);
cursor: pointer;
white-space: nowrap;
}
.arrow {
display: block;
width: 0;
height: 0;
margin: 0 auto;
border: 4px solid transparent;
border-top-width: 4px;
border-top-color: #00a75b;
}
.housename {
display: inline-block;
width: 70px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
.housenum {
display: inline-block;
width: 20px;
}
/* 房源列表样式: */
.houseList {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 330px;
transition: all 1s;
transform: translate3d(0, 330px, 0);
background: #fff;
}
.show {
transform: translate3d(0, 0, 0);
}
.titleWrap {
position: relative;
width: 100%;
background: #c0c0c2;
border-top: 1px solid #c8c8c8;
}
.listTitle {
display: inline-block;
padding-left: 10px;
line-height: 43px;
font-size: 16px;
color: #1e1e1e;
vertical-align: middle;
}
.titleMore {
float: right;
padding-right: 15px;
line-height: 43px;
font-size: 13px;
color: #1e1e1e;
vertical-align: middle;
}
.titleMore:visited {
text-decoration: none;
}
/* 房屋列表项样式 */
.houseItems {
padding: 0 10px;
overflow-y: auto;
height: 100%;
padding-bottom: 45px;
}
.house {
height: 120px;
position: relative;
box-sizing: border-box;
justify-content: space-around;
padding-top: 18px;
border-bottom: 1px solid #e5e5e5;
}
.imgWrap {
float: left;
width: 106px;
height: 80px;
}
.img {
width: 106px;
height: 80px;
}
.content {
overflow: hidden;
line-height: 22px;
padding-left: 12px;
}
.title {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
font-size: 15px;
color: #394043;
}
.desc {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
font-size: 12px;
color: #afb2b3;
}
.price {
font-size: 12px;
color: #fa5741;
}
.priceNum {
font-size: 16px;
font-weight: bolder;
}
.tag {
display: inline-block;
font-size: 12px;
border-radius: 3px;
padding: 4px 5px;
margin-right: 5px;
line-height: 12px;
}
.tag1 {
color: #39becd;
background: #e1f5f8;
}
.tag2 {
color: #3fc28c;
background: #e1f5ed;
}
.tag3 {
color: #5aabfd;
background: #e6f2ff;
}