import { Component } from 'react' ; import { requireScript } from '@uiw/react-amap-require-script' ; export interface APILoaderConfig { /**
     * akay 密钥
     * 您需先[申请密钥(ak)](https://lbs.amap.com/dev/key/app)。开发文档说明地址：https://lbs.amap.com/api/javascript-api/guide/abc/prepare
     *
     * 1. 首先，[注册开发者账号](https://lbs.amap.com/dev/id/newuser)，成为高德开放平台开发者
     * 2. 登陆之后，在进入「应用管理」 页面「创建新应用」
     * 3. 为应用[添加 Key](https://lbs.amap.com/dev/key/app)，「服务平台」一项请选择「 Web 端 ( JSAPI ) 」
     *
     */ akay : string ; default version ? : string ; /**
     * 协议，默认是根据当前网站协议的
     */ protocol ? : 'http' | 'https' ; default hostAndPath ? : string ; /**
     * JSONP 回调函数
     */ callbackName ? : string ; example plugin ? : string ; } export function delay ( time ) { return new Promise ( ( resolve , reject ) => { window . setTimeout ( resolve , time ) ; } ) ; } export interface APILoaderProps extends APILoaderConfig { /**
     * 用于展示加载中或错误状态
     */ fallback ? : ( error ? : Error ) => React . ReactNode ; } interface State { loaded : boolean ; error ? : Error ; } const DEFAULT_RETRY_TIME = 3 ; /**
 * APILoader 用于加载百度地图依赖
 */ export class APILoader extends Component < APILoaderProps > { public static defaultProps = { akay : '' , hostAndPath : 'webapi.amap.com/maps' , version : '2.0' , callbackName : 'load_amap_sdk' , plugin : '' , } ; /**
     * 全局可能存在多个 Loader 同时渲染, 但是只能由一个负责加载
     */ private static waitQueue : Array < [ Function , Function ] > = [ ] ; public state : State = { loaded : ! ! window . AMap , } ; public constructor ( props ) { super ( props ) ; if ( props . akay === null ) { throw new TypeError ( 'AMap: akay is required' ) ; } } public componentDidMount ( ) { const { callbackName } = this . props ; if ( window . AMap == null ) { if ( window [ callbackName as any ] ) { APILoader . waitQueue . push ( [ this . finish , this . handleError ] ) ; return ; } this . loadMap ( ) ; } } public render ( ) { return this . state . loaded ? ( this . props . children ) : this . props . fallback ? ( this . props . fallback ( this . state . error ) ) : this . state . error ? ( < div style = { { color : 'red' } } > { this . state . error . message } < /div>) : null; } private getScriptSrc ( ) { const cfg = this . props ; let protocol = ( cfg . protocol || window . location . protocol ) as APILoaderConfig [ 'protocol' ] ; if ( protocol ! . indexOf ( ':' ) === - 1 ) { protocol += ':' ; } let plugin = '' ; if ( cfg . plugin ) { plugin = `&plugin=${ cfg . plugin }` ; } return `${ protocol }//${ cfg . hostAndPath }?v=${ cfg . version }&key=${ cfg . akay }&callback=${ cfg . callbackName }${ plugin }` ; } private async loadMap ( ) { const { callbackName } = this . props ; const src = this . getScriptSrc ( ) ; ( window as any ) [ callbackName as any ] = ( ) => { const queue = APILoader . waitQueue ; APILoader . waitQueue = [ ] ; queue . forEach ( ( task ) => task [ 0 ] ( ) ) ; this . finish ( ) ; } ; for ( let i = 0 ; i < DEFAULT_RETRY_TIME ; i ++ ) { try { await requireScript ( src ) ; break ; } catch ( error ) { if ( i === DEFAULT_RETRY_TIME - 1 ) { const err = new Error ( `Failed to load AMap: ${ error . message }` ) ; const queue = APILoader . waitQueue ; APILoader . waitQueue = [ ] ; queue . forEach ( ( task ) => task [ 1 ] ( err ) ) ; this . handleError ( err ) ; return ; } await delay ( i * 1000 ) ; } } } private handleError = ( error ) => { this . setState ( { error } ) ; } ; private finish = ( ) => { this . setState ( { loaded : true , } ) ; } ; }