| import React from 'react' |
| |
| |
| import axios from 'axios' |
| import { Link } from 'react-router-dom' |
| |
| |
| import NavHeader from '../../components/NavHeader' |
| |
| |
| |
| 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.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()) |
| |
| |
| this.renderOverlays(value) |
| } |
| }, |
| label |
| ) |
| |
| |
| map.addEventListener('movestart', () => { |
| |
| if (this.state.isShowList) { |
| this.setState({ |
| isShowList: false |
| }) |
| } |
| }) |
| } |
| |
| |
| |
| |
| async renderOverlays(id) { |
| const res = await axios.get(`http://localhost:8080/area/map?id=${id}`) |
| |
| const data = res.data.body |
| |
| |
| const { nextZoom, type } = this.getTypeAndZoom() |
| |
| data.forEach(item => { |
| |
| this.createOverlays(item, nextZoom, type) |
| }) |
| } |
| |
| |
| |
| |
| |
| getTypeAndZoom() { |
| |
| const zoom = this.map.getZoom() |
| let nextZoom, type |
| |
| |
| if (zoom >= 10 && zoom < 12) { |
| |
| |
| nextZoom = 13 |
| |
| 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.id = id |
| |
| |
| label.setContent(` |
| <div class="${styles.bubble}"> |
| <p class="${styles.name}">${name}</p> |
| <p>${count}套</p> |
| </div> |
| `) |
| |
| |
| label.setStyle(labelStyle) |
| |
| |
| label.addEventListener('click', () => { |
| |
| this.renderOverlays(id) |
| |
| |
| this.map.centerAndZoom(point, zoom) |
| |
| |
| 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.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) { |
| 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() { |
| 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> |
| ) |
| } |
| } |
运行结果

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南