从0开始的中后台管理系统-7(订单列表功能实现,调用百度地图打点以及轨迹图动态展示)
首先要用百度地图的api去生成地图,以及地图展示缩放和折线功能的实现,就是根据百度地图api文档走就行,首先引入js库,然后给window添加上库自带的方法。
interface Window {BMapGL: {[propName: string]: any}BMapGLLib: anyBMapLib: 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() // 添加缩放控件//启用滚轮缩放 通过enableScrollwheelzoommap.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 = idconst 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 (<Modaltitle='地图打点'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 (<Modaltitle='地图打点'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) || fileNameconst 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,模拟点击就会自动下载,然后下载之后自动删除