从0开始的中后台管理系统-7(订单列表功能实现,调用百度地图打点以及轨迹图动态展示)

62 阅读4分钟

         首先要用百度地图的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,模拟点击就会自动下载,然后下载之后自动删除