import {CSSProperties, useEffect, useRef, useState} from 'react'; import * as echarts from 'echarts'; import {MapSeriesOption} from 'echarts'; import {useInterval, useSize} from 'ahooks'; import { ceil, isBoolean, isEmpty, maxBy, merge, uniqBy, size, gte, last, omit, map, } from 'lodash'; import {Empty} from 'antd'; // @ts-ignore import {getAMapLoader} from '@component/amap'; // @ts-ignore import classes from './style.module.css'; import {LOADING_COLOR, MAP_COLORS} from './type'; import getFeatures from './getFeatures'; // 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 type ECOption = echarts.ComposeOption; type Grid = { name: string; adcode: string | number; [key: string]: string | number; }; export interface MapProps { service: (params: any) => Promise; color?: Array; options?: ECOption; conditions?: any; breadcrumbClassName?: any; lastLevel?: string; goDown?: boolean; roam?: boolean; isCarousel?: boolean; isKeepGrid?: boolean; searchKey?: string; loadingType?: any; style?: CSSProperties; toolbarloop: 'top5' | 'all'; onDownCallBack?: (n: Grid, c?: string | number, o?: object) => void; } export function ChartsMap({ service, color = MAP_COLORS, conditions, options = {}, goDown = true, lastLevel = 'district', onDownCallBack, breadcrumbClassName, roam = false, isCarousel = false, style = {}, searchKey = '', loadingType = {color: LOADING_COLOR}, isKeepGrid = false, toolbarloop = 'top5' }: MapProps) { const ele = useRef(null); const echartsRef = useRef(null); const echartsInstance = useRef(null); const [empty, setEmpty] = useState(undefined); const [breadcrumb, setBreadcrumb] = useState>([]); const [interval, setInterval] = useState(undefined); const [time, setTime] = useState(undefined); const count = useRef(0); useInterval(() => { function getDataLength() { const l = size(echartsInstance.current.getOption().series[0]?.data); if (toolbarloop === 'top5') { return gte(l, 5) ? 5 : l; } else { return l; } } echartsInstance.current.dispatchAction({ type: 'hideTip', }); echartsInstance.current.dispatchAction({ type: 'downplay', seriesIndex: 0, }); echartsInstance.current.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: count.current % getDataLength(), }); echartsInstance.current.dispatchAction({ type: 'showTip', seriesIndex: 0, dataIndex: count.current % getDataLength(), position: () => { }, }); if (gte(count.current, toolbarloop === 'top5'?4:getDataLength()-1)) { count.current = 0; } else { count.current++; } }, interval); function clear(c?: boolean) { if (c) { count.current = 0; } setInterval(undefined); echartsInstance.current.dispatchAction({ type: 'hideTip', }); echartsInstance.current.dispatchAction({ type: 'downplay', seriesIndex: 0, }); } function handleBreadcrumb(grid: Grid, key: number) { setBreadcrumb(breadcrumb.slice(0, key + 1)); echartsInstance.current?.resetOption(grid); onDownCallBack && onDownCallBack(grid); } const responsive = useSize(ele.current); useInterval(() => { echartsInstance.current?.resetOption(last(breadcrumb)); }, time); useEffect(() => { echartsInstance.current?.resize(); }, [JSON.stringify(responsive)]); useEffect(() => { const params = Object.assign( conditions, isKeepGrid ? omit(last(breadcrumb), 'name') : null, ); if (!echartsRef.current) { echartsRef.current = echarts.init(ele.current); } echartsRef.current.showLoading(Object.assign({text: ''}, loadingType)); (async function init() { const geoJson: any = await getFeatures(params.adcode); if (isEmpty(geoJson)) { echartsInstance.current?.clear(); echartsRef.current.hideLoading(); setEmpty(true); return; } const properties = geoJson?.currentFeature?.properties; const {adcode, name} = properties; echarts.registerMap(name, geoJson); if (!echartsInstance.current) { echartsInstance.current = initMap( echartsRef.current, { color, service, goDown: goDown, // 是否下钻 lastLevel: lastLevel, mapName: name, // 地图名 mapCode: adcode, // 地图code callback: onDownCallBack, setBreadcrumb, roam, searchKey, loadingType, }, options, ); } if (!isEmpty(params) && echartsInstance.current) { try { const _params = { name, // 地图名 adcode: adcode, // 地图code [searchKey]: params[searchKey], // 地图code }; if (isKeepGrid) { setBreadcrumb( uniqBy([...breadcrumb, _params], (item) => String(item.adcode)), ); } else { setBreadcrumb([_params]); } function callback() { setInterval(5000); setTime(300000); } echartsInstance.current?.resetOption( _params, conditions, isCarousel && callback, ); if (isCarousel) { clear(true); echartsInstance.current.on('mouseover', () => { clear(); }); echartsInstance.current.on('mouseout', () => { setInterval(5000); }); } } catch (e) { } } })(); }, map(Object.values(conditions), String)); return (
{isBoolean(empty) && empty ? ( ) : (
)}
    {breadcrumb.map((item, key) => (
  • handleBreadcrumb(item, key)}> {key > 0 && key < breadcrumb?.length && '-'} {item?.name}
  • ))}
); } export function initMap(chart: any, customOptions: any, options: any) { let cacheConditions = {}; const option = merge( { backgroundColor: '#F5F5F5', // 画布背景色 geo: { show: true, map: customOptions.mapName, layoutSize: '100%', zoom: 1.2, roam: customOptions.roam, scaleLimit: { min: 1.2, max: 10, }, label: { show: false, }, itemStyle: { borderColor: '#c5dff9', borderWidth: 1, }, emphasis: { label: { show: false, }, itemStyle: { //区域 areaColor: '#FFDF34', }, }, select: { label: { show: false, }, itemStyle: { areaColor: '#BDBDBD', }, }, }, visualMap: { min: 0, bottom: 70, type: 'continuous', max: 100, text: ['高', '低'], top: 'bottom', itemWidth: 8, itemHeight: 320, calculable: true, realtime: false, show: true, inRange: { color: customOptions.color, }, }, series: [ { type: 'map', map: customOptions.mapName, geoIndex: 0, seriesIndex: 0, }, { type: 'effectScatter', coordinateSystem: 'geo', showEffectOn: 'render', seriesIndex: 1, roam: customOptions.roam, }, ], }, options, ); chart.setOption(option); // 添加事件 chart.on('click', async function (params: any) { const grid = params.data; if (!grid?.adcode) { return; } const {adcode, name} = grid; const geoJson: any = await getFeatures(adcode); if (isEmpty(geoJson)) { return; } const level = geoJson?.currentFeature?.properties?.level; if (level === customOptions.lastLevel) { return; } echarts.registerMap(name, geoJson); customOptions.setBreadcrumb((d: any) => { return uniqBy( [ ...d, { adcode: adcode, name, [customOptions.searchKey]: grid[customOptions.searchKey], }, ], 'adcode', ); }); customOptions.callback && customOptions.callback(grid, option, chart); chart.resetOption({ adcode: adcode, name, [customOptions.searchKey]: grid[customOptions.searchKey], }); }); chart.resetOption = async function ( grid: Grid, conditions = cacheConditions, carousel: any, ) { try { const {name, adcode} = grid; cacheConditions = {...conditions}; option.geo.map = name; chart.clear(); chart.showLoading(Object.assign({text: ''}, customOptions.loadingType)); const data = await customOptions.service({ ...cacheConditions, adcode, [customOptions.searchKey]: grid[customOptions.searchKey], }); chart.hideLoading(); const maxValue: any = maxBy(data, 'value'); option.visualMap.max = ceil(maxValue?.value) || 100; option.series[0].data = data; chart.setOption(option); carousel && carousel(); } catch (e) { console.log(e, 'error'); } }; window.onresize = function () { chart?.resize(); }; return chart; }