
1.页面容器百度地图的承载DOM在index.html中div classdriver-bmap iddriverBmap aria-label百度地图展示司机当前位置/div div classbmap-empty idbmapEmpty配置百度地图 AK 后展示司机当前位置/divdriverBmap是真实百度地图容器bmapEmpty是加载前或失败时显示的提示。页面底部还配置了百度地图AK,如下window.DRIVER_HALL_BMAP { ak: mGzBueXxvA5PDc7JOgtWDZoV35O5DTGz, };2.SDK加载流程核心函数是loadBmapSdk()function loadBmapSdk() { if (window.BMapGL) return Promise.resolve(window.BMapGL); if (state.bmapLoading) return state.bmapLoading; const bmapConfig getBmapConfig(); if (!bmapConfig.ak) return Promise.reject(new Error(请先配置百度地图 AK)); state.bmapLoading new Promise((resolve, reject) { const existing document.querySelector(#${BMAP_SCRIPT_ID}); if (existing) { existing.remove(); } const callbackName __driverHallBmapLoaded; const timeout window.setTimeout(() { delete window[callbackName]; reject(new Error(百度地图加载超时请检查 AK 白名单、网络或浏览器拦截)); }, 10000); window[callbackName] () { window.clearTimeout(timeout); if (window.BMapGL) { resolve(window.BMapGL); } else { reject(new Error(百度地图 SDK 已返回但 BMapGL 未初始化请检查 AK 类型)); } delete window[callbackName]; }; const script document.createElement(script); script.id BMAP_SCRIPT_ID; script.src https://api.map.baidu.com/api?v1.0typewebglak${encodeURIComponent(bmapConfig.ak)}callback${callbackName}; script.async true; script.onerror () { window.clearTimeout(timeout); delete window[callbackName]; reject(new Error(百度地图 SDK 加载失败请检查网络和 AK Referer 白名单)); }; document.head.appendChild(script); }); return state.bmapLoading; }流程是1.如果页面已经有window.BMapGL,说明百度地图SDK已加载直接返回。2.如果正在加载中复用state.bmapLoading,避免重复插入多个script.3.从window.DRIVER_HALL_BMAP.ak或localStorage.driver_hall_bmap_ak读取AK。4.动态创建script:https://api.map.baidu.com/api?v1.0typewebglakxxxcallback__driverHallBmapLoaded5.百度SDK加载成功后会调用_driverHallBmapLoaded。6.回调里检查window.BMapGL是否存在。7.如果10秒内没有成功提示“百度地图加载超时”。所以这里不是后端转发SDK而是浏览器直接请求百度地图官方JS。3.地图渲染流程核心函数是renderDriverBmap()async function renderDriverBmap() { if (!els.driverBmap || !els.cityMap) return; if (!state.config.token || !state.config.driverId) { setBmapMessage(); return; } const lng Number(state.config.lng); const lat Number(state.config.lat); if (!Number.isFinite(lng) || !Number.isFinite(lat)) { setBmapMessage(暂无司机定位坐标); return; } try { setBmapMessage(正在加载百度地图...); const BMapGL await loadBmapSdk(); const bdPoint gcj02ToBd09(lng, lat); const center new BMapGL.Point(bdPoint.lng, bdPoint.lat); if (!state.bmap) { state.bmap new BMapGL.Map(els.driverBmap); state.bmap.centerAndZoom(center, 15); state.bmap.enableScrollWheelZoom(true); state.bmap.addControl(new BMapGL.ScaleControl()); state.bmap.addControl(new BMapGL.ZoomControl()); state.bmapMarker new BMapGL.Marker(center, { title: 司机当前位置, }); state.bmap.addOverlay(state.bmapMarker); state.bmapLabel new BMapGL.Label(我, { position: center, offset: new BMapGL.Size(-15, -46), }); state.bmapLabel.setStyle({ width: 32px, height: 32px, border: 4px solid #fff, borderRadius: 999px, background: #151d22, color: #ffd447, boxShadow: 0 14px 28px rgba(23, 32, 38, 0.22), fontSize: 14px, fontWeight: 900, lineHeight: 24px, textAlign: center, }); state.bmap.addOverlay(state.bmapLabel); } else { state.bmap.centerAndZoom(center, 15); state.bmapMarker?.setPosition(center); state.bmapLabel?.setPosition(center); } setBmapMessage(); } catch (error) { state.bmapLoading null; els.cityMap.classList.remove(bmap-ready); console.error([DriverHall] 百度地图加载失败:, error); setBmapMessage(error.message || 百度地图加载失败); } }它的判断顺序是1.页面必须有#driverBmap容器。2.必须已经登录有token和driverId。3.必须有司机经纬度state.config.lng、state.config.lat。4.显示“正在加载百度地图...”。5.调用loadBmapSdk()加载百度地图 SDK。6.把当前坐标从 GCJ-02 转成百度 BD-09function gcj02ToBd09(lng, lat) { const xPi (Math.PI * 3000.0) / 180.0; const z Math.sqrt(lng * lng lat * lat) 0.00002 * Math.sin(lat * xPi); const theta Math.atan2(lat, lng) 0.000003 * Math.cos(lng * xPi); return { lng: z * Math.cos(theta) 0.0065, lat: z * Math.sin(theta) 0.006, }; }7.创建new BMapGL.Map(...)new BMapGL.Point(...)centerAndZoom(center, 15)缩放控件、比例尺控件司机当前位置 Marker“我”的 Label8.如果地图之前已经创建过就值更新中心点、Marker和Label的位置。这里有一个重要点当前前端没有调用浏览器原生定位navigator.geolocation地图展示的位置来自state.config.lng / state.config.lat也就是后端返回或本地保存的司机坐标。4.坐标来源登录成功后前端调用后端登录接口postPublicApi(/driver/driverLogin, { phone, password }, apiBase)然后从响应里取token driverId lng / longitude lat / latitude如果后端返回了经纬度前端就用后端经纬度否则会回退到本地已有坐标或默认上海坐标。登录后还会调用const body await postApi(/driver/auth/profile, { driver_id: Number(state.config.driverId), });这个接口再次同步司机资料包括lng、lat、service_status、昵称等。5.和后端的关联百度地图本身只在前端加载后端不参与“地图 SDK 加载”。后端主要负责三类数据第一类司机身份和坐标登录/driver/driverLogin返回token、driverId、lng、lat。前端拿到坐标后渲染百度地图。第二类司机在线状态和位置上报司机上线/下线时前端调用const body await postApi(/driver/auth/service/status, { driver_id: Number(state.config.driverId), service_status: online ? 1 : 0, lng: state.config.lng, lat: state.config.lat, });这一步才是“把司机当前位置告诉后端”。后端可以把司机位置写入数据库或 Redis GEO。代码里也有 Redis key 设计RedisKeyDriverOnline RedisKeyDriverGeo第三类附近订单查询接单大厅加载订单时前端调用const body await postApi(/driver/auth/order/pending, { longitude: state.config.lng, latitude: state.config.lat, distance: state.filters.distance || state.config.radius, min_price: state.filters.fare, sort_by: state.sortBy, page, page_size: state.pageSize, });地图订单接口调用const body await postApi(/driver/auth/order/map, { longitude: state.config.lng, latitude: state.config.lat, distance: state.filters.distance || state.config.radius, });同样传司机经纬度和范围。后端根据司机位置筛选附近订单再返回订单列表。6.当前地图能力边界当前只有“工作台首页司机当前位置”使用了真实百度地图。接单大厅的订单地图不是百度地图而是前端 CSS 模拟地图点位逻辑在renderMapMarkers()附近。订单详情页刚改的路线图也是模拟地图样式不是真实百度地图路线规划。所以现在的架构可以理解为登录/资料接口 - 获取司机 lng/lat ↓ 前端保存到 state.config ↓ 进入工作台 home ↓ 加载百度 BMapGL SDK ↓ GCJ-02 转 BD-09 ↓ 百度地图展示司机当前位置 司机上线/刷新订单 ↓ 前端把 lng/lat 传给后端 ↓ 后端用于在线司机位置、附近订单筛选、派单7.如果地图加载不出来优先查这些最常见的原因是百度地图 AK 不是 WebGL JS API 类型。AK 没配置 Referer 白名单当前访问域名不被允许。本地打开方式或端口和白名单不一致比如localhost、127.0.0.1、实际 IP 没加进去。浏览器或网络拦截https://api.map.baidu.com/api?...。页面没有登录或者后端没有返回有效lng/lat前端就不会真正渲染地图。