地图找房模块
功能:
- 展示当前定位城市
- 展示该城市所有区的房源数据
- 展示某区下所有镇的房源数据
- 展示某镇下所有小区的房源数据
- 展示某小区下的房源数据列表
涉及到的功能点:地图标注、缩放登记、缩放事件等
1,封装顶部导航栏
1,在components中创建NavHeader组件封装导航栏可复用组件
2,使用NavHeader组件
export default function NavHeader ({children, history}) {
// history是props.history
// 默认情况下,只有路由 Route 直接渲染的组件才能获取到路由信息(比如:history.go()等),如果需要在其他组件中获取到路由信息可以通过 withRouter 高阶组件来获取。
// 那么我们可以使用高阶组件处理
// 1,从 react-router-dom 中导入 withRouter 高阶组件
// 2,使用 withRouter 包裹 NavHeader 组件
// 3,从 props 中解构出 history 对象
// 4,调用 history.go() 实现返回上一页
// 5,从 props 中解构出 onLeftClick 函数,实现自定义返回按钮的点击事件
import { withRouter } from 'react-router-dom'
export default function NavHeader ({children, history, onLeftClick}) {
const defaultHandler = () => history.go(-1)
return (
<NavBar
mode="light"
icon={<i className="iconfont icon-back"/>}
onLeftClick={onLeftClick || defaultHandler}
>
{children}
</NavBar>
)
}
以上代码修改为:
function NavHeader ({children, history, onLeftClick}) {
const defaultHandler = () => history.go(-1) // 默认点击行为
return (
<NavBar
mode="light"
icon={<i className="iconfont icon-back"/>}
onLeftClick={onLeftClick || defaultHandler}
>
{children}
</NavBar>
)
}
export default withRouter(NavHeader)
}
onLeftClick点击事件,可以在使用 NavHeader 组件自定义onLeftClick事件:
// Map组件
<NavHeader onLeftClick={() => {
this.props.history.go(-1)
}}>
地图找房
</NavHeader>
添加props校验:
- 1,安装:yarn add prop-types
- 2, 导入 PropTypes
import PropTypes from 'prop-types'
- 3, 给 NavHeader 组件的 children 和 onLeftClick 属性添加 props 校验
function NavHeader ({children, history, onLeftClick}) {
const defaultHandler = () => history.go(-1)
return (
<NavBar
mode="light"
icon={<i className="iconfont icon-back"/>}
onLeftClick={onLeftClick || defaultHandler}
>
{children}
</NavBar>
)
}
NavHeader.propTypes = {
children: PropTypes.string.isRequired,
onLeftClick: PropTypes.func
}
export default withRouter(NavHeader)
组件间样式覆盖问题
css IN JS
- css IN JS:是使用 js 编写 css 的统称,用来解决 css 样式冲突、覆盖等问题
- css in js的具体实现有50多种,比如:CSS Modules、styled-components等
- 推荐使用: CSS Modules(React脚手架已集成,可直接使用)
CSS Modules:
- CSS Modules通过对css类名重命名,保证每个类名的唯一性,从而避免样式冲突的问题
- 换句话:所有类名都具有“局部作用域”,只在当前组件内部生效
- 命名采用:BEM
BEM(Block块,Element元素,Modifier三部分组成)命名规范,比如: .list_item__active
- 在React脚手架中演化为:文件名、类名、hash(随机)三部分,只需要指定类名即可
// 自动生成的类名,我们只需要提供 classname即可
[filename]_[classname]__[hash]
// 类名
.error{}
// 生成的类名:
.Button_error__ax7yz
CSS Modules在项目中的使用:
1,创建名为[name].module.css的样式文件(React脚手架中的约定,与普通CSS作区分)
在CityList组件中创建的样式文件名:
index.module.css
2,组件中导入样式文件
import styles from './index.module.css'
3,通过 styles 对象访问对象中的样式名来设置样式
<div className={styles.test}></div>
示例:
imageCityList index.js:
import styles from './index.module.css'
<div className={styles.test}>测试样式</div>
index.module.css:
.test {
color: red;
font-size: 40px;
}
- 对于组件库中已经有的全局样式(比如:.am-navnar-title),需要使用 :global() 来指定:
:global(.am-navnar-title){}
2, 根据定位展示当前城市
- 1,获取当前定位城市
- 2,获取地址解析器解析当前城市坐标
- 3,调用 centerAndZoom()方法在地图中展示当前城市,并设置缩放级别为11
- 4,在地图中添加比例尺和平移缩放控件
componentDidMount() {
// 获取当前定位城市
const {label, value} = JSON.parse(localStorage.getItem('current_city'))
//创建地址解析器实例
const map = new BMapGL.Map('container')
const myGeo = new BMapGL.Geocoder();
// 将地址解析结果显示在地图上,并调整地图视野
myGeo.getPoint(label, (point) => {
if(point){
map.centerAndZoom(point, 11);
map.addOverlay(new BMapGL.Marker(point, {title: label}))
// 添加比例尺和平移缩放控件
// 使用Map.addControl()方法向地图添加控件
map.addControl(new BMapGL.ScaleControl)
map.addControl(new BMapGL.ZoomControl)
}else{
alert('您选择的地址没有解析到结果!');
}
}, label)
}
3,创建文本覆盖物
- 1, 创建label实例对象
- 2,调用 setStyle()方法设置样式
- 3,在 map 对象上调用 addOverlay()方法,将文本覆盖物添加到地图中
const opts = {
position : point, // 指定文本标注所在的地理位置
offset : new BMap.Size(30, -30) //设置文本偏移量
}
const label = new BMapGL.Label("文本信息", opts); // 创建文本标注对象
// 设置样式
label.setStyle({
color : "red",
fontSize : "12px",
height : "20px",
lineHeight : "20px",
fontFamily:"微软雅黑"
});
// 将创建好的文本标注添加为地图的覆盖物
map.addOverlay(label);
绘制房源覆盖物
- 1,调用Label的setContent()方法,传入HTML结构,修改HTML内容的样式
// 设置房源覆盖物
Label.setContent(`
<div class="${styles.bubble}">
<p class="${styles.name}">哈哈哈</p>
<p>99套</p>
</div>
`)
- 2,调用setStyle()修改覆盖物样式
Label.setStyle()
- 3, 给文本覆盖物添加单击事件
Label.addEventListener("click", () => {
console.log('被点击了')
})
地图找房
- 1,获取房源数据,渲染覆盖物
- 2,单击覆盖物后:1,放大地图 2,获取数据,渲染下一级覆盖物
1,渲染多有区的房源覆盖物
- 1,获取房源数据
- 2,遍历数据,创建覆盖物,给每个覆盖物添加唯一标识
- 3,给覆盖物添加单击事件
- 4,在单击事件中,获取到当前单击项的唯一标识
- 5,放大地图(级别为13),调用clearOverlays()方法清除当前覆盖物
myGeo.getPoint(label, async (point) => {
if(point){
map.centerAndZoom(point, 11);
map.addOverlay(new BMapGL.Marker(point, {title: label}))
map.addControl(new BMapGL.ScaleControl)
map.addControl(new BMapGL.ZoomControl)
// 获取房源数据
const res = await axios.get(`http://localhost:8080/area/map?id=${value}`)
res.data.body.forEach(item => {
const {coord: {latitude, longitude}, label: areaname, count, value} = item
const areaPoint = new BMapGL.Point(longitude, latitude)
const opts = {
position: areaPoint, // 指定文本标注所在的地理位置
offset: new BMapGL.Size(-35, -35) //设置文本偏移量
}
const Label = new BMapGL.Label("", opts)
// 给 label对象添加一个唯一标识
Label.id = value
// 设置房源覆盖物
Label.setContent(`
<div class="${styles.bubble}">
<p class="${styles.name}">${areaname}</p>
<p>${count}套</p>
</div>
`)
Label.setStyle(labelStyle);
map.addOverlay(Label);
// 添加单击事件
Label.addEventListener("click", () => {
// 放大地图,以当前点击的覆盖物为中心放大地图
map.centerAndZoom(areaPoint, 13);
// 清除当前覆盖物信息
map.clearOverlays()
})
});
}else{
alert('您选择的地址没有解析到结果!');
}
}, label)
网友评论