react-amap海量点优化

打印 上一主题 下一主题

主题 842|帖子 842|积分 2526

前言:高版本的react-amap 支持MassMarkers 组件用于一次性添加大量的标记点。本次优化的海量点是在低版本react-amap的基础上。官方保举利用聚合useCluster属性来优化海量点的渲染。
直接附上代码:
  1. import React, { Component } from "react";
  2. import { Map, Markers, Polygon, InfoWindow } from 'react-amap'
  3. import {
  4.   Form,
  5.   Switch,
  6.   message,
  7. } from "antd";
  8. import ActionsForm from "components/RoutePanel/ActionsForm";
  9. import { MAP_AMAP_KEY, MAP_AMAP_VERSION } from 'constants/config'
  10. import {
  11.   API_FETCH_ZONES_DEPT_POINT,
  12. } from 'constants/api'
  13. import fetch from 'common/fetch'
  14. import styles from "./amap.css";
  15. import { debounce } from 'lodash';
  16. const polygonType = {
  17.   main: 'main',
  18.   adjacent: 'adjacent',
  19.   normal: 'normal',
  20. }
  21. // 获取区域样式
  22. const getPolygonStyle = (pt) => {
  23.   switch (pt) {
  24.     case polygonType.main:
  25.       return {
  26.         fillColor: '#DC2626',
  27.         fillOpacity: 0.5,
  28.         strokeOpacity: 0.5,
  29.         strokeWeight: 2,
  30.         strokeColor: '#DC2626',
  31.       }
  32.     case polygonType.adjacent:
  33.       return {
  34.         fillColor: '#34D399',
  35.         fillOpacity: 0.5,
  36.         strokeOpacity: 1,
  37.         strokeWeight: 1,
  38.         strokeColor: '#34D399',
  39.       }
  40.     case polygonType.normal:
  41.     default:
  42.       return {
  43.         fillColor: '#333333',
  44.         fillOpacity: 0.3,
  45.         strokeOpacity: 0.5,
  46.         strokeWeight: 1,
  47.         strokeColor: '#333333',
  48.       }
  49.   }
  50. }
  51. export class ZoneNeighborhoodMap extends Component {
  52.   constructor(props) {
  53.     super(props)
  54.     this.map = null; // 地图实例
  55.     this.mapEvents = {
  56.       created: (mapInstance) => {
  57.         this.map = mapInstance;
  58.         this.map.on('zoomend', this.handleZoom);
  59.       },
  60.       close: () => {
  61.         this.map = null;
  62.         if (this.map) {
  63.           this.map.off('zoomend', this.handleZoom);
  64.         }
  65.       }
  66.     };
  67.     this.state = {
  68.       infoWindowPosition: null,
  69.       infoWindowTitle: '',
  70.       polygonActive: false,
  71.       pointReferShow: true, //运输点参考
  72.       pointReferData: [],
  73.       areaReferShow: true, //已绘制参考
  74.       areaReferData: [],
  75.       locTypes: [],
  76.       data: props.data,
  77.       // infoWindowPosition: {
  78.       //   longitude: 120,
  79.       //   latitude: 30,
  80.       // },
  81.       infoWindowVisible: false,
  82.       infoWindowData: {
  83.         id: "",
  84.         desc: "",
  85.       },
  86.       infoWindow: {
  87.         position: {
  88.           longitude: 120,
  89.           latitude: 30,
  90.         },
  91.         visible: false,
  92.         data: null,
  93.       },
  94.       useCluster:false
  95.     }
  96.     this.markerEvents = {
  97.       created: (e) => {},
  98.       mouseover: (e) => {
  99.         const position = e.target.getPosition();
  100.         this.setState({
  101.           infoWindow: {
  102.             position: {
  103.               longitude: position.lng,
  104.               latitude: position.lat,
  105.             },
  106.             visible: true,
  107.             data: e.target.getExtData(),
  108.           },
  109.         });
  110.       },
  111.       mouseout: (e) => {
  112.         this.setState({
  113.           infoWindow: {
  114.             position: {
  115.               longitude: 120,
  116.               latitude: 30,
  117.             },
  118.             visible: false,
  119.             data: null,
  120.           },
  121.         });
  122.       },
  123.     };
  124.   }
  125.   
  126.   componentWillUnmount() {
  127.     if (this.map) {
  128.       this.map.destroy();
  129.     }
  130.   }
  131.   componentWillUnmount() {
  132.     if (this.map) {
  133.       this.map.destroy()
  134.     }
  135.   }
  136.   
  137.   // 缩放事件处理函数
  138.   handleZoom = () => {
  139.     const zoomLevel = this.map.getZoom();
  140.     console.log('zoomLevel',zoomLevel)
  141.     if (zoomLevel > 8) {
  142.       this.setState({ useCluster: false });
  143.     } else {
  144.       this.setState({ useCluster: true });
  145.     }
  146.   };
  147.   setFitViewWithZoneId(zoneId) {
  148.     const fitOverlays = []
  149.     this.map.getAllOverlays().forEach(polygon => {
  150.       if (polygon.getExtData().zoneId === zoneId) {
  151.         fitOverlays.push(polygon)
  152.       }
  153.     })
  154.     if (fitOverlays.length) {
  155.       this.map.setFitView(fitOverlays, false, undefined, this.map.getZoom())
  156.     }
  157.   }
  158.   renderPolygons() {
  159.     const { mainZoneId, polygons, toggleAdjacentZone, adjacentZoneIds } = this.props
  160.     const l = polygons.length
  161.     const _polygons = []
  162.     for (let i = 0; i < l; i++) {
  163.       const detail = polygons[i]
  164.       if (detail.geoArea && detail.geoArea.length) {
  165.         let _polygonType = polygonType.normal
  166.         if (detail.zoneId === mainZoneId) {
  167.           _polygonType = polygonType.main
  168.         } else if (adjacentZoneIds.includes(detail.zoneId)) {
  169.           _polygonType = polygonType.adjacent
  170.         }
  171.         detail.geoArea.forEach((path, pathId) => {
  172.           _polygons.push(
  173.             <Polygon
  174.               path={path}
  175.               key={`${detail.id}:${pathId}`}
  176.               style={getPolygonStyle(_polygonType)}
  177.               events={{
  178.                 click: () => {
  179.                   if (detail.zoneId === mainZoneId) {
  180.                     return
  181.                   }
  182.                   toggleAdjacentZone(detail.zoneId)
  183.                 },
  184.                 mousemove: (e) => {
  185.                   this.setState(() => ({
  186.                     infoWindowPosition: e.lnglat,
  187.                     infoWindowTitle: detail.zoneDesc
  188.                   }))
  189.                 },
  190.                 mouseout: () => {
  191.                   this.setState(() => ({
  192.                     infoWindowPosition: null
  193.                   }))
  194.                 }
  195.               }}
  196.               extData={{ zoneId: detail.zoneId }}
  197.             />
  198.           )
  199.         })
  200.       }
  201.     }
  202.     return _polygons
  203.   }
  204.   renderInfoWindow() {
  205.     const { infoWindowPosition, infoWindowTitle } = this.state
  206.     if (!infoWindowPosition) {
  207.       return null
  208.     }
  209.     return <InfoWindow
  210.       position={{
  211.         longitude: infoWindowPosition.lng,
  212.         latitude: infoWindowPosition.lat,
  213.       }}
  214.       isCustom={true}
  215.       content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
  216.       offset={[2,-10]}
  217.       visible
  218.     />
  219.   }
  220.   getPoint = ()=>{
  221.     fetch(API_FETCH_ZONES_DEPT_POINT, {
  222.       method: 'POST',
  223.       headers: {
  224.         'Accept': 'application/json',
  225.         'Content-Type': 'application/json'
  226.       },
  227.       body: JSON.stringify({ deptId:this.props.deptId })
  228.     }).then((response) => {
  229.       if (response.ok) {
  230.         return response.json();
  231.       }
  232.       throw new Error("Bad response from server");
  233.     })
  234.     .then((json) => {
  235.       if(json.data instanceof Array){
  236.         if (!json.data.length) {
  237.           message.info("没有可显示的运输地点");
  238.         }else{
  239.           if(json.data.length > 500){
  240.             this.setState({
  241.               useCluster: true,
  242.             })
  243.           }
  244.           json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
  245.           this.setState({pointReferData:json.data},()=>{
  246.            if (!this.map) return; // 如果地图实例未初始化,则不执行
  247.             this.map.setFitView();
  248.           })
  249.         }
  250.       }
  251.     })
  252.     .catch(e=>{})
  253.   }
  254.   renderInfo = () => {
  255.     const { position, visible, data } = this.state.infoWindow;
  256.     const {locTypes} = this.state
  257.     if (!data) {
  258.       return null;
  259.     }
  260.     const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
  261.     const tds = (
  262.       <div  className={styles.info}>
  263.         <div className='inforow'>
  264.           <span>运输地点代码</span>
  265.           <span>{data.locId}</span>
  266.         </div>
  267.         <div className='inforow'>
  268.           <span>运输地点描述</span>
  269.           <span>{data.locDesc}</span>
  270.         </div>
  271.         <div className='inforow'>
  272.           <span>类型</span>
  273.           <span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
  274.         </div>
  275.         <div className='inforow'>
  276.           <span>城市</span>
  277.           <span>{data.city}</span>
  278.         </div>
  279.         <div className='inforow'>
  280.           <span>省份</span>
  281.           <span>{data.province}</span>
  282.         </div>
  283.         <div className='inforow'>
  284.           <span>国家</span>
  285.           <span>{data.country}</span>
  286.         </div>
  287.         <div className='inforow'>
  288.           <span>经度</span>
  289.           <span>{data.longitude}</span>
  290.         </div>
  291.         <div className='inforow'>
  292.           <span>纬度</span>
  293.           <span>{data.latitude}</span>
  294.         </div>
  295.         <div className='inforow'>
  296.           <span>地址</span>
  297.           <span>{data.addr}</span>
  298.         </div>
  299.         <div className='inforow'>
  300.           <span>限行区域</span>
  301.          <span>{data.udzDesc1}({data.udz1})</span>
  302.         </div>
  303.         <div className='inforow'>
  304.           <span>业务区域</span>
  305.          <span>{data.udzDesc2}({data.udz2})</span>
  306.         </div>
  307.       </div>
  308.     );
  309.     return (
  310.       <InfoWindow
  311.         position={position}
  312.         visible={visible}
  313.         isCustom={true}
  314.         offset={[2, 2]}
  315.       >
  316.         {tds}
  317.       </InfoWindow>
  318.     );
  319.   };
  320.   pointRender = (extData) => {
  321.     return (
  322.       <div
  323.         style={{
  324.           color: "#4e72b8",
  325.           width: "8px",
  326.           height: "8px",
  327.           borderRadius: "50%",
  328.           background: "#4169E1",
  329.           textAlign: "center",
  330.         }}
  331.       />
  332.     );
  333.   };
  334.   initPointReferData = () => {
  335.     if (!this.state.pointReferShow) {
  336.       this.setState({
  337.         pointReferData: [],
  338.       });
  339.     } else {
  340.       this.getPoint()
  341.     }
  342.   };
  343.   componentDidMount() {
  344.     this.initPointReferData();
  345.   }
  346.   // 运输点参考
  347.   // changePointReferShow = (checked) => {
  348.   //   this.setState({ pointReferShow: checked }, () => {
  349.   //     this.initPointReferData();
  350.   //   });
  351.   // };
  352.   changePointReferShow = debounce((checked) => {
  353.     this.setState({ pointReferShow: checked }, () => {
  354.       this.initPointReferData();
  355.     });
  356.   }, 300); // 300ms 的防抖延迟
  357.   render() {
  358.     const {
  359.       polygonActive,
  360.       pointReferShow,
  361.       data,
  362.       pointReferData,
  363.       areaReferData,
  364.       areaReferShow,
  365.     } = this.state;
  366.     const option = {
  367.       amapkey: MAP_AMAP_KEY,
  368.       version: MAP_AMAP_VERSION,
  369.       mapStyle: 'amap://styles/whitesmoke',
  370.       // loading: this.renderLoading(),
  371.       status: {
  372.         resizeEnable: true,
  373.       },
  374.       plugins: ['ToolBar', 'Scale'],
  375.       events: this.mapEvents,
  376.     }
  377.     return (
  378.       <div style={{ width: "100%", height: "100vh", position: "relative" }}>
  379.         <ActionsForm>
  380.             <Form layout="inline">
  381.               <Form.Item>
  382.                 <Switch
  383.                   checkedChildren="显示地点"
  384.                   unCheckedChildren="显示地点"
  385.                   checked={pointReferShow}
  386.                   onChange={this.changePointReferShow}
  387.                 />
  388.               </Form.Item>
  389.             </Form>
  390.         </ActionsForm>
  391.         <Map {...option}>
  392.           <Markers
  393.           markers={pointReferData}
  394.           offset={[0,0]}
  395.           render={this.pointRender}
  396.           events={this.markerEvents}
  397.           useCluster={this.state.useCluster}
  398.         />
  399.         {this.renderPolygons()}
  400.         {this.renderInfoWindow()}
  401.         {this.renderInfo()}
  402.     </Map>
  403.       </div>
  404.     );
  405.   }
  406. }
复制代码
希望点的数量少于1000时直接展示所有点,不聚合
  1. import React, { Component } from "react";
  2. import PropTypes from "prop-types";
  3. import Immutable from 'seamless-immutable';
  4. import {
  5.   Map,
  6.   Polygon,
  7.   Markers,
  8.   PolyEditor,
  9.   MouseTool,
  10.   InfoWindow,
  11. } from "react-amap";
  12. import {
  13.   Form,
  14.   Button,
  15.   Switch,
  16.   Tooltip,
  17.   Modal,
  18.   message,
  19.   Menu,
  20.   Dropdown,
  21.   Popconfirm,
  22.   Icon,
  23. } from "antd";
  24. import isomorphicFetch from "isomorphic-fetch";
  25. import {
  26.   API_FETCH_DOMAIN,
  27.   API_FETCH_ZONES_DEPT_POINT,
  28.   API_FETCH_ZONES_GROUP
  29. } from 'constants/api'
  30. import fetch from 'common/fetch'
  31. import {
  32.   MAP_AMAP_KEY,
  33.   MAP_AMAP_DRIVING_KEY,
  34.   MAP_AMAP_VERSION,
  35. } from "constants/config";
  36. import ActionsForm from "components/RoutePanel/ActionsForm";
  37. import ZoneCascader from "components/PcaCascader/ZoneCascader";
  38. import pstyles from "./polygons.scss";
  39. // 获取封闭polygon(首尾点坐标一致)
  40. const _getClosedPolygon = (paths) => {
  41.   if (Array.isArray(paths) && paths.length > 2) {
  42.     const firstLocation = paths[0];
  43.     const lastLocation = paths[paths.length - 1];
  44.     if (
  45.       firstLocation[0] === lastLocation[0] &&
  46.       firstLocation[1] === lastLocation[1]
  47.     ) {
  48.       return paths;
  49.     } else {
  50.       return [...paths, firstLocation];
  51.     }
  52.   } else {
  53.     return [];
  54.   }
  55. };
  56. class PolygonEdit extends Component {
  57.   constructor(props) {
  58.     super(props);
  59.     this.state = {
  60.       polygonActive: false,
  61.       pointReferShow: true, //运输点参考
  62.       pointReferData: [],
  63.       areaReferShow: false, //已绘制参考
  64.       areaReferData: [],
  65.       locTypes: [],
  66.       data: props.data,
  67.       mapEditable: props.mapEditable,
  68.       deleteMenuVisible: false,
  69.       hoverItemData: [],
  70.       adcode: "",
  71.       province: "",
  72.       provinceCode:"",
  73.       city: "",
  74.       cityCode:"",
  75.       area: "",
  76.       street: "",
  77.       polyline: "",
  78.       infoWindowPosition: {
  79.         longitude: 120,
  80.         latitude: 30,
  81.       },
  82.       infoWindowVisible: false,
  83.       infoWindowData: {
  84.         id: "",
  85.         desc: "",
  86.       },
  87.       infoWindow: {
  88.         position: {
  89.           longitude: 120,
  90.           latitude: 30,
  91.         },
  92.         visible: false,
  93.         data: null,
  94.       },
  95.       showLabels: true, // 默认显示悬浮名称
  96.       useCluster:false
  97.     };
  98.     const _this = this;
  99.     this.amap = null; // 地图
  100.     this.mouseTool = null; // 鼠标工具
  101.     this.amapEvents = {
  102.       created: (mapInstance) => {
  103.         _this.amap = mapInstance;
  104.         _this.amap.on('zoomend', this.handleZoom);
  105.       },
  106.       close: () => {
  107.         _this.amap = null;
  108.         if (_this.amap) {
  109.           _this.amap.off('zoomend', this.handleZoom);
  110.         }
  111.       },
  112.     };
  113.     this.polyEditorEvents = {
  114.       created: (ins) => {
  115.         _this.amap.setFitView();
  116.       },
  117.       addnode: () => {},
  118.       adjust: ({ lnglat, pixel, type, target } = option) => {},
  119.       removenode: () => {},
  120.       end: ({ type, target }) => {
  121.         this.polyEditorEnd(target, type);
  122.       },
  123.     };
  124.     this.polygonEvents = {
  125.       created: (ins) => {
  126.         _this.amap.setFitView();
  127.       },
  128.     };
  129.     this.toolEvents = {
  130.       created: (tool) => {
  131.         _this.mouseTool = tool;
  132.       },
  133.       draw({ obj }) {
  134.         _this.drawWhat(obj);
  135.       },
  136.     };
  137.     this.markerEvents = {
  138.       created: (e) => {},
  139.       mouseover: (e) => {
  140.         const position = e.target.getPosition();
  141.         this.setState({
  142.           infoWindow: {
  143.             position: {
  144.               longitude: position.lng,
  145.               latitude: position.lat,
  146.             },
  147.             visible: true,
  148.             data: e.target.getExtData(),
  149.           },
  150.         });
  151.       },
  152.       mouseout: (e) => {
  153.         this.setState({
  154.           infoWindow: {
  155.             position: {
  156.               longitude: 120,
  157.               latitude: 30,
  158.             },
  159.             visible: false,
  160.             data: null,
  161.           },
  162.         });
  163.       },
  164.     };
  165.   }
  166.   componentWillUnmount() {
  167.     if (this.amap) {
  168.       this.amap.destroy();
  169.     }
  170.   }
  171.   componentDidMount() {
  172.     if(this.props.isGroup){
  173.       this.initPointReferData();
  174.       this.initAreaReferData()
  175.     }
  176.     this.getLocType()
  177.   }
  178.    // 缩放事件处理函数
  179.   handleZoom = () => {
  180.     const zoomLevel = this.amap.getZoom();
  181.     console.log('zoomLevel', zoomLevel);
  182.     console.log('this.state.pointReferData.length', this.state.pointReferData.length);
  183.   
  184.     // 判断点的数量是否小于1000,以及当前缩放等级
  185.     if (this.state.pointReferData.length > 1000 && zoomLevel < 10) {
  186.       this.setState({ useCluster: true });
  187.     } else {
  188.       this.setState({ useCluster: false });
  189.     }
  190.   };
  191.   getPoint = ()=>{
  192.     fetch(API_FETCH_ZONES_DEPT_POINT, {
  193.       method: 'POST',
  194.       headers: {
  195.         'Accept': 'application/json',
  196.         'Content-Type': 'application/json'
  197.       },
  198.       body: JSON.stringify({ deptId:this.props.deptId })
  199.     }).then((response) => {
  200.       if (response.ok) {
  201.         return response.json();
  202.       }
  203.       throw new Error("Bad response from server");
  204.     })
  205.     .then((json) => {
  206.       if(json.data instanceof Array){
  207.         if (!json.data.length) {
  208.           message.info("没有可显示的运输地点");
  209.         }else{
  210.           json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
  211.           this.setState({pointReferData:json.data},()=>{
  212.             if (!this.amap) return; // 如果地图实例未初始化,则不执行
  213.             this.amap.setFitView();
  214.           })
  215.         }
  216.       }
  217.     })
  218.     .catch(e=>{})
  219.   }
  220.   renderPolygons = () => {
  221.     const { areaReferShow, areaReferData } = this.state;
  222.     const GROUP_KEY = {
  223.       unConfiguredGroups: 'unConfiguredGroups',
  224.       configuredGroups: 'configuredGroups'
  225.     }
  226.     const prepareGroups = (groups) => {
  227.       const _groups = Immutable
  228.         .asMutable(groups, { deep: true })
  229.         .map(group => ({
  230.           ...group,
  231.           zoneDetail: (group.zoneDetail || []).map(detail => {
  232.             return {
  233.               ...detail,
  234.               geoArea: JSON.parse(detail.geoArea)
  235.             }
  236.           })
  237.         }))
  238.       return {
  239.         groups,
  240.         [GROUP_KEY.unConfiguredGroups]: _groups
  241.           .filter(group => !group.lines.length)
  242.           .map(group => ({
  243.             ...group,
  244.             lines: []
  245.           })),
  246.         [GROUP_KEY.configuredGroups]: _groups.filter(group => group.lines.length),
  247.         zoneOptions: _groups.map(group => ({
  248.           value: group.zoneId,
  249.           label: group.zoneDesc,
  250.         })),
  251.         polygons: _groups.reduce((polygons, group) => {
  252.           polygons.push(...group.zoneDetail.map(zoneDetail => ({ ...zoneDetail, zoneDesc: group.zoneDesc }))) // flat zoneDetail
  253.      
  254.           return polygons
  255.         }, [])
  256.       }
  257.     }
  258.     const newData = prepareGroups(areaReferData)
  259.     const polygons = newData.polygons
  260.     const l = polygons.length
  261.     const _polygons = []
  262.     for (let i = 0; i < l; i++) {
  263.       const detail = polygons[i]
  264.       if (detail.geoArea && Array.isArray(detail.geoArea) && detail.geoArea.length) {
  265.         detail.geoArea.forEach((path, pathId) => {
  266.           _polygons.push(
  267.             <Polygon
  268.               path={path}
  269.               key={`${detail.id}:${pathId}`}
  270.               style={{
  271.                 fillColor: '#333333',
  272.                 fillOpacity: 0.3,
  273.                 strokeOpacity: 0.5,
  274.                 strokeWeight: 1,
  275.                 strokeColor: '#333333',
  276.               }}
  277.               events={{
  278.                 mousemove: (e) => {
  279.                   this.setState(() => ({
  280.                     infoWindowPosition: e.lnglat,
  281.                     infoWindowTitle: detail.zoneDesc
  282.                   }))
  283.                 },
  284.                 mouseout: () => {
  285.                   this.setState(() => ({
  286.                     infoWindowPosition: null
  287.                   }))
  288.                 }
  289.               }}
  290.               extData={{ zoneId: detail.zoneId }}
  291.             />
  292.           )
  293.         })
  294.       }else{
  295.         console.log('detail.geoArea',detail.geoArea)
  296.       }
  297.     }
  298.   return _polygons
  299.   
  300.   }
  301.   getArea = ()=>{
  302.     fetch(API_FETCH_ZONES_GROUP, {
  303.       method: 'POST',
  304.       credentials: 'include',
  305.       headers: {
  306.         'Accept': 'application/json',
  307.         'Content-Type': 'application/json'
  308.       },
  309.       body: JSON.stringify({ branchId:this.props.branchId,deptId:this.props.deptId})
  310.     }).then((response) => {
  311.       if (response.ok) {
  312.         return response.json();
  313.       }
  314.       throw new Error("Bad response from server");
  315.     })
  316.     .then((json) => {
  317.       if(json.data && json.data.data instanceof Array){
  318.         if (!json.data.data.length) {
  319.           message.info("没有可显示的区域");
  320.         }else{
  321.           this.setState({areaReferData:json.data.data})
  322.         }
  323.       }
  324.     })
  325.     .catch(e=>{
  326.       console.log('e',e)
  327.     })
  328.   }
  329.   renderInfoWindow() {
  330.     const { infoWindowPosition, infoWindowTitle, showLabels } = this.state
  331.     if(showLabels){
  332.       if (!infoWindowPosition) {
  333.         return null
  334.       }
  335.       return <InfoWindow
  336.         position={{
  337.           longitude: infoWindowPosition.lng,
  338.           latitude: infoWindowPosition.lat,
  339.         }}
  340.         isCustom={true}
  341.         content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
  342.         offset={[2,2]}
  343.         visible
  344.       />
  345.     }
  346.   }
  347.   initPointReferData = () => {
  348.     if (!this.state.pointReferShow) {
  349.       this.setState({
  350.         pointReferData: [],
  351.       });
  352.     } else {
  353.       this.getPoint()
  354.     }
  355.   };
  356.   initAreaReferData = () => {
  357.     if (!this.state.areaReferShow) {
  358.       this.setState({
  359.         areaReferData: [],
  360.       });
  361.     } else {
  362.       this.getArea()
  363.     }
  364.   };
  365.   renderLoading = () => {
  366.     const loadingStyle = {
  367.       position: "relative",
  368.       height: "100%",
  369.       width: "100%",
  370.       display: "flex",
  371.       justifyContent: "center",
  372.       alignItems: "center",
  373.     };
  374.     return <div style={loadingStyle}>Loading Map...</div>;
  375.   };
  376.   // 运输点参考
  377.   changePointReferShow = (checked) => {
  378.     this.setState({ pointReferShow: checked }, () => {
  379.       this.initPointReferData();
  380.     });
  381.   };
  382.   // 已绘制区域参考
  383.   changeAreaReferShow = (checked) => {
  384.     this.setState({ areaReferShow: checked }, () => {
  385.       this.initAreaReferData();
  386.     });
  387.   };
  388.    // 是否悬浮显示名称
  389.   toggleLabels = (checked) => {
  390.     this.setState({ showLabels: checked });
  391.   };
  392.   pointRender = (extData) => {
  393.     return (
  394.       <div
  395.         style={{
  396.           color: "#4e72b8",
  397.           width: "8px",
  398.           height: "8px",
  399.           borderRadius: "50%",
  400.           background: "#4169E1",
  401.           textAlign: "center",
  402.         }}
  403.       />
  404.     );
  405.   };
  406.   getLocType=()=>{
  407.     fetch(`${API_FETCH_DOMAIN}LOCATION_TYPE`,{
  408.       credentials: 'include'
  409.     })
  410.     .then((response) => {
  411.       if (response.ok) {
  412.         return response.json();
  413.       }
  414.       throw new Error("Bad response from server");
  415.     })
  416.     .then((json) => {
  417.       if(json.data instanceof Array){
  418.         this.setState({locTypes:json.data})
  419.       }
  420.     })
  421.     .catch(e=>{})
  422.   }
  423.   renderInfo = () => {
  424.     const { position, visible, data } = this.state.infoWindow;
  425.     const {locTypes} = this.state
  426.     if (!data) {
  427.       return null;
  428.     }
  429.     const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
  430.     const tds = (
  431.       <div  className={pstyles.pinfo}>
  432.         <div className='inforow'>
  433.           <span>运输地点代码</span>
  434.           <span>{data.locId}</span>
  435.         </div>
  436.         <div className='inforow'>
  437.           <span>运输地点描述</span>
  438.           <span>{data.locDesc}</span>
  439.         </div>
  440.         <div className='inforow'>
  441.           <span>类型</span>
  442.           <span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
  443.         </div>
  444.         <div className='inforow'>
  445.           <span>城市</span>
  446.           <span>{data.city}</span>
  447.         </div>
  448.         <div className='inforow'>
  449.           <span>省份</span>
  450.           <span>{data.province}</span>
  451.         </div>
  452.         <div className='inforow'>
  453.           <span>国家</span>
  454.           <span>{data.country}</span>
  455.         </div>
  456.         <div className='inforow'>
  457.           <span>经度</span>
  458.           <span>{data.longitude}</span>
  459.         </div>
  460.         <div className='inforow'>
  461.           <span>纬度</span>
  462.           <span>{data.latitude}</span>
  463.         </div>
  464.         <div className='inforow'>
  465.           <span>地址</span>
  466.           <span>{data.addr}</span>
  467.         </div>
  468.         <div className='inforow'>
  469.           <span>限行区域</span>
  470.          <span>{data.udzDesc1}({data.udz1})</span>
  471.         </div>
  472.         <div className='inforow'>
  473.           <span>业务区域</span>
  474.          <span>{data.udzDesc2}({data.udz2})</span>
  475.         </div>
  476.       </div>
  477.     );
  478.     return (
  479.       <InfoWindow
  480.         position={position}
  481.         visible={visible}
  482.         isCustom={true}
  483.         offset={[2, 2]}
  484.       >
  485.         {tds}
  486.       </InfoWindow>
  487.     );
  488.   };
  489.   // polygon edit switch
  490.   togglePolygon = (checked, e) => {
  491.     if (checked) {
  492.       // open edit and close add if it is drawing
  493.       if (this.mouseTool) {
  494.         this.mouseTool.close();
  495.       }
  496.       this.setState({ polygonActive: true, isDrawingPolygon: false });
  497.     } else {
  498.       // close edit
  499.       this.setState({ polygonActive: false });
  500.     }
  501.   };
  502.   // polygon add switch
  503.   toggleDrawPolygon = (checked, e) => {
  504.     if (checked) {
  505.       if (this.mouseTool) {
  506.         this.mouseTool.polygon();
  507.         this.setState({ isDrawingPolygon: true });
  508.         message.success("鼠标左键双击或右键单击完成当前多边形!");
  509.       }
  510.     } else {
  511.       if (this.mouseTool) {
  512.         this.mouseTool.close();
  513.         this.setState({ isDrawingPolygon: false });
  514.       }
  515.     }
  516.   };
  517.   // finish polygon draw
  518.   drawWhat = (obj) => {
  519.     const paths = obj.getPath();
  520.     let data = this.state.data.slice();
  521.     const pathData = paths.map((item) => [item.lng, item.lat]);
  522.     if (pathData.length > 2) {
  523.       if (this.amap) {
  524.         this.amap.remove(obj);
  525.       }
  526.       data.push(_getClosedPolygon(pathData));
  527.       this.setState({ data });
  528.       message.success(
  529.         `您成功绘制了一个${
  530.           paths.length
  531.         }边形,可继续绘制或点击结束多边形绘制按钮`
  532.       );
  533.     }
  534.   };
  535.   // polygon editor end
  536.   polyEditorEnd = (target, type) => {
  537.     const paths = target.getPath();
  538.     let isSinglePath = false; // 是否是单围栏
  539.     const pathData = paths.map((item, index) => {
  540.       if (Array.isArray(item)) {
  541.         const itemPaths = item.map((element) => [element.lng, element.lat]);
  542.         return _getClosedPolygon(itemPaths);
  543.       } else {
  544.         isSinglePath = true;
  545.         return [item.lng, item.lat];
  546.       }
  547.     });
  548.     this.setState({
  549.       data: isSinglePath ? [_getClosedPolygon(pathData)] : pathData,
  550.     });
  551.   };
  552.   // 多边形删除下拉菜单
  553.   deleteMenuVisibleChange = (flag) => {
  554.     this.setState({ deleteMenuVisible: flag });
  555.   };
  556.   // 删除多边形
  557.   handleMenuClick = (e) => {
  558.     this.handleDeletePath(e.key);
  559.   };
  560.   handleDeletePath = (key) => {
  561.     const path = [...this.state.data];
  562.     path.splice(key, 1);
  563.     this.setState({ data: path, deleteMenuVisible: false });
  564.   };
  565.   handleMenuHover = (key) => {
  566.     this.setState({ hoverItemData: this.state.data[key] });
  567.   };
  568.   handleChangeAdcode = ({ adcode, province, city, area, street,provinceCode,cityCode }) => {
  569.     this.setState({ adcode, province, city, area, street,provinceCode, cityCode});
  570.   };
  571.   handleAdcodeCancle = () => {
  572.     this.setState({
  573.       adcode: "",
  574.       province: "",
  575.       provinceCode:"",
  576.       city: "",
  577.       cityCode:"",
  578.       area: "",
  579.       street: "",
  580.     });
  581.   };
  582.   handleAdcodeConfirm = () => {
  583.     const { adcode, provinceCode, cityCode, area, street, mapEditable } = this.state;
  584.     if (adcode) {
  585.       this.setState({
  586.         mapEditable: "N",
  587.         adcode: "",
  588.         polygonActive: false,
  589.         isDrawingPolygon: false,
  590.       });
  591.       if (street) {
  592.         isomorphicFetch(`/city-tiles/${provinceCode}_${cityCode}.json`)
  593.           .then((response) => {
  594.             if (response.ok) {
  595.               return response.json();
  596.             }
  597.             throw new Error("Bad response from server");
  598.           })
  599.           .then((json) => {
  600.             if (json.status === "0") {
  601.               throw new Error("No districts from server");
  602.             }
  603.             try {
  604.               const streetItemData = json.features.find(
  605.                 (s) => s.properties.subdistrict == street
  606.               ).geometry.coordinates;
  607.               if (this.mouseTool) {
  608.                 this.mouseTool.close();
  609.               }
  610.               this.setState({
  611.                 data: streetItemData, // some data
  612.               });
  613.               if (this.amap) {
  614.                 this.amap.setFitView();
  615.               }
  616.             } catch (e) {
  617.               message.error("获取行政区划数据失败!");
  618.             }
  619.             // const polyline = (json.districts && json.districts[0]) ? json.districts[0].polyline : [];
  620.             // const data = polyline.split('|').map(block => block.split(';').map(pointer => pointer.split(',').map(lnglat => Number(lnglat))));
  621.             // if (this.mouseTool){
  622.             //   this.mouseTool.close();
  623.             // }
  624.             // this.setState({
  625.             //   data: data, // some data
  626.             // })
  627.             // if (this.amap) {
  628.             //   this.amap.setFitView();
  629.             // }
  630.           })
  631.           .catch((error) => {
  632.             message.error("获取行政区划数据失败!");
  633.             this.setState({
  634.               mapEditable: "Y",
  635.               province: "",
  636.               provinceCode:"",
  637.               city: "",
  638.               cityCode:"",
  639.               area: "",
  640.               street: "",
  641.               data: [],
  642.             });
  643.           });
  644.       } else {
  645.         // fetch polyline data
  646.         isomorphicFetch(
  647.           `//restapi.amap.com/v3/config/district?key=e17fafe279209e4b3a303cc907347277&keywords=${adcode}&subdistrict=0&extensions=all`
  648.         )
  649.           .then((response) => {
  650.             if (response.ok) {
  651.               return response.json();
  652.             }
  653.             throw new Error("Bad response from server");
  654.           })
  655.           .then((json) => {
  656.             if (json.status === "0") {
  657.               throw new Error("No districts from server");
  658.             }
  659.             const polyline =
  660.               json.districts && json.districts[0]
  661.                 ? json.districts[0].polyline
  662.                 : [];
  663.             const data = polyline
  664.               .split("|")
  665.               .map((block) =>
  666.                 block
  667.                   .split(";")
  668.                   .map((pointer) =>
  669.                     pointer.split(",").map((lnglat) => Number(lnglat))
  670.                   )
  671.               );
  672.             if (this.mouseTool) {
  673.               this.mouseTool.close();
  674.             }
  675.             this.setState({
  676.               data: data, // some data
  677.             });
  678.             if (this.amap) {
  679.               this.amap.setFitView();
  680.             }
  681.           })
  682.           .catch((error) => {
  683.             message.error("获取行政区划数据失败!");
  684.             this.setState({
  685.               mapEditable: "Y",
  686.               province: "",
  687.               provinceCode:"",
  688.               city: "",
  689.               cityCode:"",
  690.               area: "",
  691.               street: "",
  692.               data: [],
  693.             });
  694.           });
  695.       }
  696.     } else {
  697.       if (mapEditable === "N") {
  698.         this.setState({
  699.           mapEditable: "Y",
  700.           adcode: "",
  701.           province: "",
  702.           provinceCode:"",
  703.           city: "",
  704.           cityCode:"",
  705.           area: "",
  706.           street: "",
  707.           data: [],
  708.         });
  709.       }
  710.     }
  711.   };
  712.   handleFinished = () => {
  713.     const {
  714.       polygonActive,
  715.       data,
  716.       adcode,
  717.       province,
  718.       city,
  719.       area,
  720.       street,
  721.       mapEditable,
  722.     } = this.state;
  723.     let result = {
  724.       geoArea:
  725.         Array.isArray(data) && data.length > 0 ? JSON.stringify(data) : null,
  726.       mapEditable,
  727.     };
  728.     if (mapEditable === "N") {
  729.       Object.assign(result, {
  730.         country: "中国",
  731.         state: province,
  732.         city,
  733.         district: area,
  734.         subdistrict: street,
  735.       });
  736.     }
  737.     if (polygonActive) {
  738.       this.setState({ polygonActive: false });
  739.       Modal.confirm({
  740.         title: "您的多边形调整还未结束,是否结束?",
  741.         onOk: () => {
  742.           this.props.onConfirm(result);
  743.         },
  744.         onCancel: () => {
  745.           this.setState({ polygonActive: true });
  746.         },
  747.       });
  748.     } else {
  749.       this.props.onConfirm(result);
  750.     }
  751.   };
  752.   render() {
  753.     const {
  754.       polygonActive,
  755.       isDrawingPolygon,
  756.       pointReferShow,
  757.       data,
  758.       pointReferData,
  759.       areaReferData,
  760.       areaReferShow,
  761.       deleteMenuVisible,
  762.       hoverItemData,
  763.       adcode,
  764.       mapEditable,
  765.       infoWindowPosition,
  766.       infoWindowVisible,
  767.       infoWindowData,
  768.       showLabels,
  769.       useCluster
  770.     } = this.state;
  771.     const { onCancle, isGroup } = this.props;
  772.     const option = {
  773.       amapkey: MAP_AMAP_KEY,
  774.       version: MAP_AMAP_VERSION,
  775.       mapStyle: "amap://styles/whitesmoke",
  776.       loading: this.renderLoading(),
  777.       status: {
  778.         resizeEnable: true,
  779.       },
  780.       plugins: ["ToolBar", "Scale"],
  781.       events: this.amapEvents,
  782.     };
  783.     return (
  784.       <div style={{ width: "100%", height: "100vh", position: "relative" }}>
  785.         <ActionsForm>
  786.           {isGroup ? (
  787.             <Form layout="inline">
  788.               <Form.Item>
  789.                 <Popconfirm
  790.                   title={
  791.                     <ZoneCascader
  792.                       adcode={adcode}
  793.                       onChange={this.handleChangeAdcode}
  794.                     />
  795.                   }
  796.                   icon={
  797.                     <Tooltip title="选择行政区划。此操作将首先清空已有围栏,已选择的行政区划围栏不允许编辑">
  798.                       <Icon type="question-circle-o" />
  799.                     </Tooltip>
  800.                   }
  801.                   onCancel={this.handleAdcodeCancle}
  802.                   onConfirm={this.handleAdcodeConfirm}
  803.                 >
  804.                   <Button type="primary">行政区划选择{isGroup}</Button>
  805.                 </Popconfirm>
  806.               </Form.Item>
  807.               <Form.Item>
  808.                 <Switch
  809.                   disabled={mapEditable === "N"}
  810.                   checkedChildren="调整"
  811.                   unCheckedChildren="调整"
  812.                   checked={polygonActive}
  813.                   onChange={this.togglePolygon}
  814.                 />
  815.               </Form.Item>
  816.               <Form.Item>
  817.                 <Switch
  818.                   disabled={mapEditable === "N"}
  819.                   checkedChildren="新增"
  820.                   unCheckedChildren="新增"
  821.                   checked={isDrawingPolygon}
  822.                   onChange={this.toggleDrawPolygon}
  823.                 />
  824.               </Form.Item>
  825.               <Form.Item>
  826.                 <Dropdown
  827.                   overlay={
  828.                     <Menu onClick={this.handleMenuClick}>
  829.                       {data &&
  830.                         data.length > 0 &&
  831.                         data.map((item, index) => (
  832.                           <Menu.Item key={index}>
  833.                             <span
  834.                               onMouseOver={() => this.handleMenuHover(index)}
  835.                             >{`${index + 1} 删除`}</span>
  836.                           </Menu.Item>
  837.                         ))}
  838.                     </Menu>
  839.                   }
  840.                   onVisibleChange={this.deleteMenuVisibleChange}
  841.                   visible={deleteMenuVisible}
  842.                   disabled={mapEditable === "N"}
  843.                 >
  844.                   <Button>删除</Button>
  845.                 </Dropdown>
  846.               </Form.Item>
  847.               <Form.Item>
  848.                 <Button onClick={onCancle}>取消</Button>
  849.               </Form.Item>
  850.               <Form.Item>
  851.                 <Button type="primary" onClick={this.handleFinished}>
  852.                   完成
  853.                 </Button>
  854.               </Form.Item>
  855.               <Form.Item>
  856.                 <Switch
  857.                   checkedChildren="显示地点"
  858.                   unCheckedChildren="显示地点"
  859.                   checked={pointReferShow}
  860.                   onChange={this.changePointReferShow}
  861.                 />
  862.               </Form.Item>
  863.               <Form.Item>
  864.                 <Switch
  865.                   checkedChildren="显示其他区域"
  866.                   unCheckedChildren="显示其他区域"
  867.                   checked={areaReferShow}
  868.                   onChange={this.changeAreaReferShow}
  869.                 />
  870.               </Form.Item>
  871.               <Form.Item>
  872.                 <Switch
  873.                   checkedChildren="显示区域名称"
  874.                   unCheckedChildren="隐藏区域名称"
  875.                   checked={showLabels}
  876.                   onChange={this.toggleLabels}
  877.                 />
  878.               </Form.Item>
  879.             </Form>
  880.           ) : (
  881.             <Form layout="inline">
  882.               <Form.Item>
  883.                 <Button type="primary" onClick={onCancle}>
  884.                   关闭
  885.                 </Button>
  886.               </Form.Item>
  887.             </Form>
  888.           )}
  889.         </ActionsForm>
  890.         <Map {...option}>
  891.           <Markers
  892.             markers={pointReferData}
  893.             offset={[0,0]}
  894.             render={this.pointRender}
  895.             events={this.markerEvents}
  896.             useCluster={useCluster}
  897.           />
  898.           {isGroup && <MouseTool events={this.toolEvents} />}
  899.           {isGroup ? (
  900.             <Polygon
  901.               path={data}
  902.               style={{ fillOpacity: 0.3, strokeOpacity: 0.5, strokeWeight: 1 }}
  903.               events={this.polygonEvents}
  904.             >
  905.               <PolyEditor
  906.                 active={
  907.                   polygonActive && data.length > 0 && data.flat().length <= 100
  908.                 }
  909.                 events={this.polyEditorEvents}
  910.               />
  911.             </Polygon>
  912.           ) : (
  913.             data.map((item, index) => (
  914.               <Polygon
  915.                 events={this.polygonEvents}
  916.                 key={index}
  917.                 path={item}
  918.                 style={{
  919.                   fillOpacity: 0.3,
  920.                   strokeOpacity: 0.5,
  921.                   strokeWeight: 1,
  922.                 }}
  923.               />
  924.             ))
  925.           )}
  926.           <Polygon
  927.             visible={deleteMenuVisible}
  928.             path={[hoverItemData]}
  929.             zIndex={100}
  930.             style={{
  931.               fillColor: "red",
  932.               fillOpacity: 0.3,
  933.               strokeOpacity: 0.5,
  934.               strokeWeight: 1,
  935.             }}
  936.           />
  937.           {this.renderInfo()}
  938.           {this.renderPolygons()}
  939.           {this.renderInfoWindow()}
  940.         </Map>
  941.       </div>
  942.     );
  943.   }
  944. }
  945. PolygonEdit.propTypes = {
  946.   isGroup: PropTypes.bool, // 是:data 为单个 polygon 数据;否: data 为多个 polygon 数据
  947.   data: PropTypes.array,
  948.   onCancle: PropTypes.func,
  949.   onConfirm: PropTypes.func,
  950. };
  951. PolygonEdit.defaultProps = {
  952.   isGroup: true,
  953.   data: [],
  954.   onCancle: () => {},
  955.   onConfirm: () => {},
  956. };
  957. export default PolygonEdit;
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表