react使用高德地图react-amap:Map、Markers、Circle、ContextMenu、自定义ContextMenu

 

React AMap — 基于 React 的高德地图组件

目录

搜索区域代码

地图区域代码

自定义菜单样式


复杂而且不好口述,直接上代码,后面出视频

搜索区域代码

// ------------------外部资源
import React, { useEffect } from 'react'
import { observer } from 'mobx-react-lite'
import { Form, Button, Input, Select } from 'antd'
const { Option } = Select
// ------------------内部公共
import DataSelect from "@liepin/react-dataselect-pc";

// ------------------内部私有
import store from '../../store'

export default observer(() => {
  const [form] = Form.useForm()

  const onEmployeeChange = () => {
    form.setFieldsValue({ customerId: undefined })
    store.setStore({ customerId: '' })
    onSearchParamsChange()
  }

  // 搜索客户
  const onSearchParamsChange = () => {
    const data = form.getFieldsValue(true)
    data.area = undefined
    data.range = undefined
    if (data.employeeId) {
      store.setStore({ employeeId: data.employeeId })
      store.getCustomerLocationList(data)
    } else {

      store.setStore({
        employeeId: '',
        customerId: '',
        visitInfo: {},
        customerLocationList: []
      })
    }
  }

  // 客户
  const onCustomerChange = (value) => {
    store.setStore({ customerId: value })
  }

  // 重置
  const handleClear = () => {
    const employeeId = form.getFieldsValue(true).employeeId
    form.resetFields()
    form.setFieldsValue({ employeeId })
    store.setStore({ customerId: null, range: undefined, area: '' })
    employeeId && store.getCustomerLocationList({ employeeId })
  }

  useEffect(() => {
    if (store.useInfo?.isSale) {
      form.setFieldsValue({
        employeeId: store.useInfo?.employeeId
      })
      store.getCustomerLocationList({ employeeId: store.useInfo?.employeeId })
    }
  }, [store.useInfo])

  useEffect(() => {
    form.setFieldsValue({ range: store.range, area: store.area })
  }, [store.range, store.area])

  return (
    <>
      <div className="search-block">
        <Form
          layout="inline"
          form={form}
        >
          <Form.Item label="销售" name="employeeId">
            <DataSelect
              selectType="selectEmployee"
              width={200}
              onChange={onEmployeeChange}
            />
          </Form.Item>
          <Form.Item label="合作状态" name="customerSignType">
            <Select placeholder="请选择" allowClear onChange={onSearchParamsChange} style={
   
   { width: 100 }}>
              <Option value={0}>未合作</Option>
              <Option value={1}>合作中</Option>
              <Option value={2}>合作结束</Option>
            </Select>
          </Form.Item>
          <Form.Item label="客户" name="customerId">
            <DataSelect
              value={store.customerId}
              selectType="selectEmployee"
              width={200}
              url="/customermap/search-customer.json"
              ajaxData={
   
   {
                initCondition: 'Init_LptCustomer',
                queryField: 'name',
                queryType: 'select_customer',
                isNeedPerssion: true,
                extendCondition: JSON.stringify([{"queryField":"creatorId","queryOption":"=","queryValue": store.employeeId}])
              }}
              onChange={onCustomerChange}
            />
          </Form.Item>
          <Form.Item name="area">
            <Input placeholder="区域/地点" allowClear
                   onChange={(e) => store.setStore({ area: e.target.value })} />
          </Form.Item>
          <Form.Item name="range">
            <Select placeholder="周边" allowClear style={
   
   { width: 100 }}
                    onChange={(value) => store.setStore({ range: value })}
            >
              <Option value={1}>1公里以内</Option>
              <Option value={2}>2公里以内</Option>
              <Option value={3}>3公里以内</Option>
              <Option value={4}>4公里以内</Option>
              <Option value={5}>5公里以内</Option>
            </Select>
          </Form.Item>
          <Form.Item>
            <Button onClick={handleClear}>清除</Button>
          </Form.Item>
        </Form>
      </div>

      <style jsx>{`
        .search-block {
          padding: 10px 20px;
        }
      `}</style>
    </>
  )
})

地图区域代码

// ------------------外部资源
import React, { useState, useEffect, useRef } from 'react';
import { observer } from 'mobx-react-lite'
import { toJS } from "mobx";
import {Modal, Spin } from 'antd';
import { Map, Markers, Circle } from 'react-amap';
import { useDebounceFn } from "ahooks";
// ------------------内部公共
import EditVisitPlan from "@components/EditVisitPlan";
import mark_gray from '@static/images/mark_gray.png'
import mark_green from '@static/images/mark_green.png'
// ------------------内部私有
import store from '../../store'
import './index.less'
let currentMarkerData = {}
let markerMap = []
let MapsOption = {}

// 自定义菜单
const CustomContextMenu = ({ coverLocations, mapsOption, showRange, addVisit, setCurrentMarkerData }) => {
  return (
    <ul className="custom-amap-menu"
        style={
   
   {
          left: mapsOption.pixel.x + 5,
          top: mapsOption.pixel.y
        }}>
      {
        coverLocations.map(item => {
          const row = JSON.parse(item)
          return <li key={row.customerId}>
            <span>{row.customerName}</span>
            <ul className="customer-menus">
              <li onClick={() => {
                setCurrentMarkerData?.(row)
                showRange?.()
                }}>显示周边范围</li>
              <li onClick={() => {
                setCurrentMarkerData?.(row)
                addVisit?.()
              }}>增加拜访计划</li>
              <li><a href={`/customer/todetail/?customerId=${row.customerId}&isinfoEdit=1`} target="_blank">重新维护地址</a></li>
              <li><a href={`/customervisitplan/listall/?customer_id=${row.customerId}`} target="_blank">查看拜访记录</a></li>
              <li><a href={`/customer/todetail/?customerId=${row.customerId}`} target="_blank">查看客户详情</a></li>
            </ul>
          </li>
        })
      }
    </ul>
  )
}

export default observer(() => {
  const mapInstance = useRef(null); // created map 记录map实例instance
  const contextMenu = useRef(null); // 菜单
  const [zoom, setZoom] = useState(11) // 缩放
  const [coordinates, setCoordinates] = useState([]) // 当前坐标点
  const [radius, setRadius] = useState(0) // 半径范围range
  const [markers, setMarkers] = useState([]) // Markers 数据
  const [visible, setVisible] = useState(false) // Circle 显示
  const [editVisitPlanVisible, setEditVisitPlanVisible] = useState(false) // 增加拜访计划显示
  const [customContextMenuVisible, setCustomContextMenuVisible] = useState(false) // 自定义菜单显示

  // 监听客户位置列表变化,控制地图显示
  useEffect(() => {
    getMarkers()
    store?.visitInfo?.city && debounceSetAddrLocation.run(store?.visitInfo?.city)
  }, [store.customerLocationList])

  // 监听客户变化
  useEffect(() => {
    if (markers.length && (store.customerId || store.customerId === 0)) {
      const customer = markers.find(item => {
        return item?.extData?.customerId === Number(store.customerId)
      })
      console.log('customer', customer);

      if (customer) {
        setZoom(16)
        setCoordinates(customer.position)
        store.setStore({ range: 3, customer })
        setVisible(true)

        setIconRed(customer, 500)

      } else {
        store.setStore({ customer: {} })
        Modal.warning({
          content: (<div>当前所选客户还没有坐标,<a href={`/customer/todetail/?customerId=${store.customerId}&isinfoEdit=1`} target="_blank">现在去维护</a></div>),
          okText: '取消'
        });
      }
    } else {
      setZoom(11)
      setCoordinates([116.397428, 39.90923])
      store.setStore({ range: undefined, customer: {} })
      setVisible(false)
    }
  }, [store.customerId])

  // 标红点
  const setIconRed = (customer, timeout) => {
    if (!Object.keys(store.customer).length) return
    setTimeout(() => {
      // 标红
      for(const item of markerMap) {
        if (customer?.extData?.isCover) {
          if (item?.w?.extData?.position.join() === customer?.position?.join()) {
            item.render(() => {
              return <img src='//webapi.amap.com/theme/v1.3/markers/n/mark_r.png' />
            })
          }
        } else {
          if (item?.w?.extData?.extData.customerId === customer?.extData?.customerId) {
            item.render(() => {
              return <img src='//webapi.amap.com/theme/v1.3/markers/n/mark_r.png' />
            })
          }
        }
      }
    }, timeout)
  }

  // 监听区域、地点变化,获取对应经纬点
  useEffect(() => {
    store?.area && debounceSetAddrLocation.run(store?.area);
  }, [store.area])

  // 监听周边变化,设置Circle⭕️半径
  useEffect(() => {
    setRadius(store.range * 1000)
  }, [store.range])

  // 监听经纬坐标、区域变化,控制Circle⭕️显示
  useEffect(() => {
    if (coordinates && store.area) {
      setZoom(16);
      setVisible(true)
    } else {
      setVisible(false)
    }
  }, [store.area])

  // 获取地图 Markers
  const getMarkers = () => {
    const list = []
    for(const item of toJS(store.customerLocationList)) {
      const position = item.location?.split(',')
      if (position.length === 2) {
        list.push({
          position,
          extData: item
        })
      }
    }
    setMarkers(list)
    store.setStore({ area: '' })
    store.setStore({ range: undefined })
  }

  // render动态修改标记的外观icon
  const renderMarkerFn = (extData) => {
    let icon = '//webapi.amap.com/theme/v1.3/markers/n/mark_bs.png'
    if (extData?.extData?.visitPlanStatus === 1) {
      icon = mark_gray
    } else if (extData?.extData?.visitPlanStatus === 2) {
      icon = mark_green
    }
    return <img src={icon} />
  };

  // 根据城市名做地理/逆地理编码 获取经纬坐标
  const setAddrLocation = (area) => {
    window.AMap.plugin('AMap.Geocoder', () => {
      let geocoder = new window.AMap.Geocoder({
        // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
        city: store?.visitInfo?.city
      });
      // 使用geocoder做地理/逆地理编码
      geocoder.getLocation(area, (status, result) => {
        if (status === 'complete' && result.info === 'OK') {
          const location = result.geocodes[0].location;
          setCoordinates([location.lng, location.lat])
          if (store.area && result.info === 'OK') store.setStore({ range: 3 })
        }
      });
    });
  };

  const debounceSetAddrLocation = useDebounceFn(setAddrLocation);

  // 显示周边范围
  const showRange = () => {
    setZoom(16);
    setCoordinates(currentMarkerData.location.split(','));
    store.setStore({ range: 3, customerId: currentMarkerData.customerId })
    setVisible(true)
  }

  // 增加拜访计划
  const addVisit = () => {
    setEditVisitPlanVisible(true)
  }

  // Markers 事件
  const events = {
    created: (markers) => {
      for (const item of markers) {
        markerMap.push(item)
      }
    },
    click: (mapsOption, marker) => {
      currentMarkerData = marker.getExtData().extData
      setCustomContextMenuVisible(false)
      if (currentMarkerData.isCover === 1) {
        const customAmapMenu = document.getElementsByClassName('custom-amap-menu')
        if (customAmapMenu.length === 0) {
          MapsOption = mapsOption
          setCustomContextMenuVisible(true)
        }
      } else {
        contextMenu.current.open(mapInstance.current, mapsOption.lnglat);
      }
    },
    mouseover: (mapsOption, marker) => {
      marker.setLabel({
        offset: new window.AMap.Pixel(20, 20), // 修改label相对于maker的位置
        content: marker.w.extData.extData.showName
      })
      marker.setTop(true);
    },
    mouseout: (mapsOption, marker) => {
      marker.setLabel({
        content: null
      });
      marker.setTop(false);
    }
  }

  // 地图事件
  const mapEvents = {
    created: (instance) => {
      mapInstance.current = instance;
      contextMenu.current = new window.AMap.ContextMenu();
      contextMenu.current.addItem("显示周边范围", showRange, 0);
      contextMenu.current.addItem("增加拜访计划", addVisit, 1);
      contextMenu.current.addItem("重新维护地址", function() {
        window.open(`/customer/todetail/?customerId=${currentMarkerData.customerId}&isinfoEdit=1`);
      }, 2);
      contextMenu.current.addItem("查看拜访记录", function() {
        window.open(`/customervisitplan/listall/?customer_id=${currentMarkerData.customerId}`);
      }, 3);
      contextMenu.current.addItem("查看客户详情", function() {
        window.open(`/customer/todetail/?customerId=${currentMarkerData.customerId}`);
      }, 4);
    },
    click: () => setCustomContextMenuVisible(false),
    mapmove: () => setCustomContextMenuVisible(false),
    zoomchange: () => {
      const list = [...markers]
      console.log('mapInstance.current.getZoom()', mapInstance.current.getZoom());
      setZoom(mapInstance.current.getZoom());
      if (mapInstance.current.getZoom() > 12) {
        for(const item of list) {
          item.label = {
            content: item.extData.showName,
            offset: new window.AMap.Pixel(20, 20)
          }
        }
      } else {
        for(const item of list) {
          item.label = undefined
        }
      }
      setMarkers(list)
      console.log('keys', );
      setIconRed(store.customer)
      setCustomContextMenuVisible(false)
    },
  };

  return (
    <Spin spinning={store.loading} >
      <div id="customer-maps" style={
   
   {width: '100%', height: 'calc(100vh - 184px)'}}>
        <div style={
   
   { display: store.customerLocationList.length ? 'block' : 'none', height: '100%', width: '100%' }}>
          <Map
            amapkey="0fe0e92d23dc183b6384d51515676b81"
            version="1.4.0"
            plugins={['Scale', 'ToolBar']}
            center={coordinates}
            isHotspot={true}
            zoom={zoom}
            events={mapEvents}
          >
            <Markers
              markers={markers}
              events={events}
              render={renderMarkerFn}
            />
            <Circle
              center={ coordinates }
              radius={ radius }
              visible={ visible }
              style={
   
   {
                strokeColor: "#F33", // 线颜色
                strokeOpacity: 1, // 线透明度
                strokeWeight: 3, // 线粗细度
                fillColor: "#ee2200", // 填充颜色
                fillOpacity: 0.15 // 填充透明度
              }}
              draggable={ false }
              bubble
            />
          </Map>
        </div>

        {/* 增加拜访计划 */}
        {
          editVisitPlanVisible &&
          <EditVisitPlan
            visible={editVisitPlanVisible}
            params={
   
   {
              customerId: currentMarkerData.customerId,
              customerName: currentMarkerData.customerName,
              title: '增加拜访计划',
              isEdit: false,
              isPhone: true,
              onSave: () => setEditVisitPlanVisible(false),
              onCancel: () => setEditVisitPlanVisible(false)
            }}
          />
        }

        {/* 重复坐标点的自定义菜单 */}
        {
          customContextMenuVisible &&
          <CustomContextMenu
            mapsOption={MapsOption}
            coverLocations={ currentMarkerData.coverLocations }
            showRange={showRange}
            addVisit={addVisit}
            setCurrentMarkerData={(data) => currentMarkerData = data}
          />
        }
      </div>
    </Spin>
  )
})

自定义菜单样式

#customer-maps {
  position: relative;
  .custom-amap-menu {
    position: absolute;
    top: 100px;
    left: 300px;
    border: 1px solid #ccc;
    background: #fff;
    z-index: 111;
    padding: 0;

    ul, li {
      padding: 0;
      margin: 0;
      list-style:none
    }

    li {
      height: 30px;
      line-height: 30px;
      padding: 0 10px;
      position: relative;
    }

    li:hover {
      background: #eee;
    }

    li:hover ul {
      display: block;
    }

    ul {
      width: 100px;
      position: absolute;
      top: 0;
      right: -100px;
      display: none;
      background: #fff;
      border: 1px solid #ccc;
      z-index: 111;
    }

    ul li {
      height: 30px;
      line-height: 30px;
      padding: 0 10px;
      text-align: center;
      cursor: pointer;
    }

    ul li:hover {
      background: #eee;
    }
    a {
      color: #5f6d80;
    }
    a:hover {
      text-decoration: none;
    }
    ul li a {
      display: block;
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_41887214/article/details/120556191