首先要用百度地图的api去生成地图,以及地图展示缩放和折线功能的实现,就是根据百度地图api文档走就行,首先引入js库,然后给window添加上库自带的方法。
interface Window {
BMapGL: {
[propName: string]: any
}
BMapGLLib: any
BMapLib: any
}
然后就是调用这个BMapGL方法去生成地图实例对象,去绑定到对应的div标签容器渲染地图,然后传入参数就可以展示出我们想要的地图效果了。
1.地图打点
先看效果图。
编辑
在生成地图实例对象之后,我们添加点击事件,然后点击的当前元素就包含了点击的经纬度,lng和lat,那么我们就可以把每次点击生成的经纬度推进一个数组,这样不仅仅可以去动态的生成点,还可以保存点的数组发到数据库中保存,不会丢失。这样折现图可以根据数组去生成。
import { Modal } from 'antd'
import { useImperativeHandle, useState } from 'react'
import api from '@/api/orderApi'
import { Order } from '@/types/api'
import { message } from '@/utils/AntdGlobal'
import type { IDetailProp } from '@/types/modal'
export default function OrderMarker(props: IDetailProp) {
const [visble, setVisible] = useState(false)
const [orderId, setOrderId] = useState('')
//markers状态保存你的经纬度以及当前设置id给每一个按钮
const [markers, setMarkers] = useState<
{ lng: string; lat: string; id: number }[]
>([])
useImperativeHandle(props.mRef, () => {
return {
open
}
})
// 弹框
const open = async (orderId: string) => {
setOrderId(orderId)
setVisible(true)
const detail = await api.getOrderDetail(orderId)
renderMap(detail)
}
// 渲染地图
const renderMap = (detail: Order.OrderItem) => {
//首先初始化地图 通过容器 new BMapGL.Map('markerMap')
const map = new window.BMapGL.Map('markerMap')
//设置城市中心点 也可以是一个坐标 12缩放等级
map.centerAndZoom(detail.cityName, 12)
//地图控件 比如 比例尺
const scaleCtrl = new window.BMapGL.ScaleControl() // 添加比例尺控件
map.addControl(scaleCtrl)
//缩放比例尺
const zoomCtrl = new window.BMapGL.ZoomControl() // 添加缩放控件
//启用滚轮缩放 通过enableScrollwheelzoom
map.enableScrollWheelZoom()
map.addControl(zoomCtrl)
detail.route?.map(item => {
createMarker(map, item.lng, item.lat)
})
// 绑定事件
//点击事件发生的时候e就是点击事件发生的对象元素,里面有经纬度
map.addEventListener('click', function (e: any) {
createMarker(map, e.latlng.lng, e.latlng.lat)
})
}
// 创建marker覆盖物
//生成覆盖物需要经纬度 宽和线
const createMarker = (map: any, lng: string, lat: string) => {
const id = Math.random()
const marker = new window.BMapGL.Marker(new window.BMapGL.Point(lng, lat))
markers.push({ lng, lat, id })
marker.id = id
const markerMenu = new window.BMapGL.ContextMenu()
markerMenu.addItem(
new window.BMapGL.MenuItem('删除', function () {
map.removeOverlay(marker)
const index = markers.findIndex(item => item.id === marker.id)
markers.splice(index, 1)
setMarkers([...markers])
})
)
setMarkers([...markers])
marker.addContextMenu(markerMenu)
map.addOverlay(marker)
}
// 更新打点
const handleOk = async () => {
await api.updateOrderInfo({
orderId,
route: markers
})
message.success('打点成功')
handleCancel()
}
// 关闭弹框
const handleCancel = () => {
setVisible(false)
setMarkers([])
}
return (
<Modal
title='地图打点'
width={1100}
open={visble}
okText='确定'
cancelText='取消'
onOk={handleOk}
onCancel={handleCancel}
>
<div id='markerMap' style={{ height: 500 }}></div>
</Modal>
)
}
/*
首先初始化地图 通过容器 new BMapGL.Map('markerMap')
然后第二步设置中心点
*/
主要通过在生成数组的时候,也要调用路由去把生成的route数组保存到对应的订单中,然后方便地图折线生成。
2.地图折线
根据打点生成的数组获取之后遍历出来,根据实例demo直接套用使用。
import type { IDetailProp } from '@/types/modal'
import { Modal } from 'antd'
import React, { useImperativeHandle, useState } from 'react'
import api from '@/api/orderApi'
import { message } from '@/utils/AntdGlobal'
import type { Order } from '@/types/api'
export default function OrderRoute(props: IDetailProp) {
const [visible, setVisible] = useState(false)
const [trackAni, setTrackAni] = useState<{
cancel: () => void
}>()
useImperativeHandle(props.mRef, () => {
return {
open
}
})
const open = async (orderId: string) => {
const detail = await api.getOrderDetail(orderId)
if (detail.route.length > 0) {
setVisible(true)
setTimeout(() => {
renderMap(detail)
})
} else {
message.info('请先去打点')
}
}
const renderMap = (detail: Order.OrderItem) => {
//创建地图实例对象
const map = new window.BMapGL.Map('orderRouteMap')
map.centerAndZoom(detail.cityName, 17) // 初始化地图,设置中心点坐标和地图级别
map.enableScrollWheelZoom(true) // 开启鼠标滚轮缩放
const path = detail.route || []
let point = []
for (let i = 0; i < path.length; i++) {
point.push(new window.BMapGL.Point(path[i].lng, path[i].lat))
}
const pl = new window.BMapGL.Polyline(point, {
strokeWeight: '8', //px单位
strokeOpacity: 0.8,
strokeColor: 'skyblue'
})
setTimeout(start, 1000)
function start() {
const trackAni = new window.BMapGLLib.TrackAnimation(map, pl, {
overallView: true,
tilt: 30,
duration: 20000,
delay: 300
})
trackAni.start()
setTrackAni(trackAni)
}
}
const handleCancel = () => {
setVisible(false)
trackAni?.cancel()
}
return (
<Modal
title='地图打点'
width={1100}
open={visible}
footer={false}
onCancel={handleCancel}
>
<div id='orderRouteMap' style={{ height: 500 }}></div>
</Modal>
)
}
这样关键的编辑
打点和轨迹就实现了。还有一个导出。
downloadFile(url: string, data: any, fileName = 'fileName.xlsx') {
instance({
url,
data,
method: 'post',
responseType: 'blob'
}).then(response => {
const blob = new Blob([response.data], {
type: response.data.type
})
const name = (response.headers['file-name'] as string) || fileName
const link = document.createElement('a')
link.download = decodeURIComponent(name)
link.href = URL.createObjectURL(blob)
document.body.append(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href)
})
}
}
通过访问路由,服务器把传递过去的表单数据改为2进制流Blob,然后前端把二进制流包装成Blob对象,然后创建a标签 把路径设置为url,模拟点击就会自动下载,然后下载之后自动删除