提交 198eb1d0 作者: test

feat: 优化 Mapbox 组件设计

1. 采用统一队列处理所有逻辑层到视图层通信
2. 地图配置项新增 onSourceRequestHandle 实现监听 setGeoJSONSourceForRequest 触发的请求回执
3. 新增 setGeoJSONSourceForRequest 过滤器配置,允许过滤掉接口得到的部分数据后再返回到逻辑层,优化配置项统一请求
上级 c376cf1e
import type mapboxgl from 'mapbox-gl' import type mapboxgl from 'mapbox-gl'
import type { MapboxConfig, MapboxInstance } from './index' import qs from 'qs'
import type { GeoJSONSourceDataUrlParams, MapboxConfig, MapboxInstance } from './index'
import { API_URL, API_URL_PREFIX } from '/@/utils/net'
import { isProdMode } from '@/utils/env' import { isProdMode } from '@/utils/env'
import { getToken } from '@/utils/auth'
// 组件名称 // 组件名称
export const name = 'Mapbox' export const name = 'Mapbox'
...@@ -49,16 +52,22 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>( ...@@ -49,16 +52,22 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>(
removeSource: (id: string) => getInstance()?.removeSource(id), removeSource: (id: string) => getInstance()?.removeSource(id),
setGeoJSONSourceForRequest: ( setGeoJSONSourceForRequest: (
id: string, id: string,
url: string, config: string | GeoJSONSourceDataUrlParams,
map: ( handler?: (
data: Recordable, data: Recordable,
) => GeoJSON.Feature<GeoJSON.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry>, ) => GeoJSON.Feature<GeoJSON.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry>,
) => getInstance()?.setGeoJSONSourceForRequest(id, url, map), filters?: string[],
) =>
getInstance()?.setGeoJSONSourceForRequest(id, buildGeoJSONSourceDataUrl(config), handler, [
'jsonObject',
'liveVos',
...(filters || []),
]),
setGeoJSONSourceData: (id: string, data: mapboxgl.GeoJSONSource['setData'], filter?: string) => setGeoJSONSourceData: (id: string, data: mapboxgl.GeoJSONSource['setData'], filter?: string) =>
getInstance()?.setGeoJSONSourceData(id, data, filter), getInstance()?.setGeoJSONSourceData(id, data, filter),
setVectorTileSourceTiles: (id: string, tiles: string[]) => setVectorTileSourceTiles: (id: string, tiles: string[]) =>
getInstance()?.setVectorTileSourceTiles(id, tiles), getInstance()?.setVectorTileSourceTiles(id, tiles),
addLayer: (layer: mapboxgl.Layer) => getInstance()?.addLayer(layer), addLayer: (layer: mapboxgl.Layer, beforeId?: string) => getInstance()?.addLayer(layer, beforeId),
removeLayer: (id: string) => getInstance()?.removeLayer(id), removeLayer: (id: string) => getInstance()?.removeLayer(id),
setPaintProperty: (layerId: string, name: string, value: any) => setPaintProperty: (layerId: string, name: string, value: any) =>
getInstance()?.setPaintProperty(layerId, name, value), getInstance()?.setPaintProperty(layerId, name, value),
...@@ -70,13 +79,14 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>( ...@@ -70,13 +79,14 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>(
] ]
} }
export function buildGeoJSONSourceDataUrl(config: { url: string; params: Recordable }): string { export function buildGeoJSONSourceDataUrl(config: GeoJSONSourceDataUrlParams | string): string {
const { url, params } = config const baseURL = `${API_URL}${API_URL_PREFIX}`
const keys = Object.keys(params) const defaultParams = {
if (keys.length === 0) { Authorization: getToken(),
return url
} }
const query = keys.map((key) => `${key}=${params[key]}`).join('&') const url = typeof config === 'string' ? config : config.url
return `${url}?${query}` const params = typeof config === 'string' ? defaultParams : { ...defaultParams, ...config.params }
const query = qs.stringify(params, { addQueryPrefix: true })
return `${baseURL}${url}${query}`
} }
...@@ -826,11 +826,18 @@ export interface MapboxConfig { ...@@ -826,11 +826,18 @@ export interface MapboxConfig {
// =============== 自定义事件 ================= // =============== 自定义事件 =================
/** /**
* 注意: 此处暴露的 map 对象的类型定义只是为了方便 IDE 提示, 实际上 map 对象上的方法和属性并不完整,依赖于 Mapbox 自定义组件的内部实现 * 地图加载完成事件
* @param map 组件实例
* @param data 事件数据, 由视图层在 Map 加载完成后传入的可序列化 JSON 数据 * @param data 事件数据, 由视图层在 Map 加载完成后传入的可序列化 JSON 数据
*/ */
onLoaded?: (map: MapboxInstance, data: Recordable) => void onLoaded?: (data: Recordable) => void
/**
* 监听请求数据源的请求回执
* @param id 数据源 ID
* @param url 请求地址
* @param data 请求回来的数据(默认情况下将过滤掉一些大字段或数据集,例如:jsonObject、liveVos)
*/
onSourceRequestHandle?: <T = Recordable>(data: { id: string; url: string; data: T }) => void
} }
/** /**
...@@ -879,14 +886,16 @@ export interface MapboxInstance { ...@@ -879,14 +886,16 @@ export interface MapboxInstance {
* @param id 数据源 ID * @param id 数据源 ID
* @param url 数据源 URL,通常是一个后端的数据查询接口(色斑图查询接口、站点数据查询接口) * @param url 数据源 URL,通常是一个后端的数据查询接口(色斑图查询接口、站点数据查询接口)
* @param map 数据映射函数,用于处理后端返回的数据,将其转换为 GeoJSON 数据 * @param map 数据映射函数,用于处理后端返回的数据,将其转换为 GeoJSON 数据
* @param filters 过滤对象,用于过滤响应 onSourceRequestHandle 事件时的数据,默认过滤掉 jsonObject、liveVos 字段
* @link https://docs.mapbox.com/mapbox-gl-js/api/map/#map#addsource * @link https://docs.mapbox.com/mapbox-gl-js/api/map/#map#addsource
*/ */
setGeoJSONSourceForRequest: ( setGeoJSONSourceForRequest: (
id: string, id: string,
url: string, url: string | GeoJSONSourceDataUrlParams,
map: ( map?: (
data: Recordable, data: Recordable,
) => GeoJSON.Feature<GeoJSON.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry> | any, ) => GeoJSON.Feature<GeoJSON.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry> | any,
filters?: string[],
) => void ) => void
/** /**
* 设置 GeoJSONSource 数据源 data * 设置 GeoJSONSource 数据源 data
...@@ -941,6 +950,11 @@ export interface MapboxInstance { ...@@ -941,6 +950,11 @@ export interface MapboxInstance {
setLayoutProperty: (layerId: string, name: string, value: any) => void setLayoutProperty: (layerId: string, name: string, value: any) => void
} }
export interface GeoJSONSourceDataUrlParams {
url: string
params: Recordable
}
/** /**
* 站点数据类型定义 * 站点数据类型定义
*/ */
......
import axios from 'axios' import axios from 'axios'
import { merge } from 'lodash-es' import { merge, omit } from 'lodash-es'
import { HandlerUtil, defaultStyle, loadMapControl, loadMapboxLibs } from '/@/components/Map/Mapbox' import { HandlerUtil, defaultStyle, loadMapControl, loadMapboxLibs } from '/@/components/Map/Mapbox'
// renderjs 官方文档 // renderjs 官方文档
...@@ -8,8 +8,12 @@ import { HandlerUtil, defaultStyle, loadMapControl, loadMapboxLibs } from '/@/co ...@@ -8,8 +8,12 @@ import { HandlerUtil, defaultStyle, loadMapControl, loadMapboxLibs } from '/@/co
// https://juejin.cn/post/7049185827582115870 // https://juejin.cn/post/7049185827582115870
async function request(url, handler) { async function request(url, handler) {
const res = await axios.get(url) const { data } = await axios.get(url)
if (data.status !== 200) {
throw new Error(data.message)
}
const { body } = data
if (handler) { if (handler) {
// TODO: 待优化 // TODO: 待优化
// 1. 考虑是否为函数 // 1. 考虑是否为函数
...@@ -20,10 +24,10 @@ async function request(url, handler) { ...@@ -20,10 +24,10 @@ async function request(url, handler) {
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
const executableFunction = eval(`(${handler})`) const executableFunction = eval(`(${handler})`)
return executableFunction(res.data) return { data: executableFunction(body), raw: body }
} }
return res.data return { data: body, raw: body }
} }
const defaultEmptyGeoJSON = { const defaultEmptyGeoJSON = {
...@@ -149,19 +153,19 @@ export default { ...@@ -149,19 +153,19 @@ export default {
} }
}, },
changeFlyToOptions(options) { changeFlyToOptions(options) {
this.emitChangeEvent('flyTo') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.flyTo(options) this.map.flyTo(options)
} }
}, },
changeAddSourceOptions(options) { changeAddSourceOptions(options) {
this.emitChangeEvent('addSource') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.addSource(options.id, options.source) this.map.addSource(options.id, options.source)
} }
}, },
changeSetGeoJSONSourceForRequestOptions(options) { changeSetGeoJSONSourceForRequestOptions(options) {
this.emitChangeEvent('setGeoJSONSourceForRequest') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
if (this.map.getSource(options.id)) { if (this.map.getSource(options.id)) {
// 清空数据 // 清空数据
...@@ -175,31 +179,37 @@ export default { ...@@ -175,31 +179,37 @@ export default {
} }
// 请求数据 // 请求数据
request(options.url, options.handler).then((data) => { request(options.url, options.handler).then(({ data, raw }) => {
this.map.getSource(options.id).setData(data) this.map.getSource(options.id).setData(data)
this.$ownerInstance.callMethod('onSourceRequestHandle', {
id: options.id,
url: options.url,
data: omit(raw, options.filters || []),
})
}) })
} }
}, },
changeSetGeoJSONSourceDataOptions(options) { changeSetGeoJSONSourceDataOptions(options) {
this.emitChangeEvent('setGeoJSONSourceData') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.getSource(options.id).setData(options.data) this.map.getSource(options.id).setData(options.data)
} }
}, },
changeSetVectorTileSourceTilesOptions(options) { changeSetVectorTileSourceTilesOptions(options) {
this.emitChangeEvent('setVectorTileSourceTiles') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.getSource(options.id).setTiles(options.tiles) this.map.getSource(options.id).setTiles(options.tiles)
} }
}, },
changeAddLayerOptions(options) { changeAddLayerOptions(options) {
this.emitChangeEvent('addLayer') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.addLayer(options.layer, options.beforeId) this.map.addLayer(options.layer, options.beforeId)
console.log('options', options)
} }
}, },
changeRemoveOptions(options) { changeRemoveOptions(options) {
this.emitChangeEvent('remove') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
if (options.type === 'layer') { if (options.type === 'layer') {
this.map.removeLayer(options.id) this.map.removeLayer(options.id)
...@@ -209,25 +219,25 @@ export default { ...@@ -209,25 +219,25 @@ export default {
} }
}, },
changeSetFilterOptions(options) { changeSetFilterOptions(options) {
this.emitChangeEvent('filter') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.setFilter(options.layer, options.filter) this.map.setFilter(options.layer, options.filter)
} }
}, },
changeSetLayoutPropertyOptions(options) { changeSetLayoutPropertyOptions(options) {
this.emitChangeEvent('setLayoutProperty') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.setLayoutProperty(options.layer, options.name, options.value) this.map.setLayoutProperty(options.layer, options.name, options.value)
} }
}, },
changeSetPaintPropertyOptions(options) { changeSetPaintPropertyOptions(options) {
this.emitChangeEvent('setPaintProperty') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
this.map.setPaintProperty(options.layer, options.name, options.value) this.map.setPaintProperty(options.layer, options.name, options.value)
} }
}, },
changeEventOptions(options) { changeEventOptions(options) {
this.emitChangeEvent('event') this.emitChangeEvent(options.fn)
if (this.checkOnChangeValidity(options)) { if (this.checkOnChangeValidity(options)) {
if (options.event === 'on') { if (options.event === 'on') {
if (options.type === 'click') { if (options.type === 'click') {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论