提交 aea522a2 作者: 方治民

feat: 新增 Echarts 组件,完善 Mapbox 组件及小部件

上级 19ce46c4
export * from './src/hook'
export { default as Echarts } from './src/index.vue'
import { merge } from 'lodash-es'
import { loadEchartsLibs } from '/@/components/Echarts/src/tools'
import * as EchartsHelper from './inject'
// 全局注入工具函数
window.EchartsHelper = EchartsHelper
export default {
data() {
return {
option: {},
}
},
methods: {
loadLibs: loadEchartsLibs,
init() {
// 如果已经初始化过了,就直接更新配置项
if (this.chart && this.chart.setOption) {
this.chart.setOption(this.option)
return
}
// 初始化组件
const chart = window.echarts.init(document.getElementById(this.option.id))
// 设置图表配置项
chart.setOption(this.option)
this.chart = chart
},
changeOption(option) {
if (!option?.id) {
return
}
this.option = merge(this.option, option)
if (typeof window.echarts === 'object' && typeof window.echarts.init === 'function') {
this.init()
} else {
this.loadLibs().then(() => {
this.init()
})
}
},
},
}
import type { EChartsOption } from 'echarts'
import { tryOnMounted } from '@vueuse/core'
import { isProdMode } from '@/utils/env'
// 组件名称
export const name = 'Echarts'
export interface EchartsInstance {
setOption: (option: Partial<EChartsOption>) => Promise<void>
}
/**
* 注册 Echarts 实例
* @param option Echarts 配置项
*/
export function useEcharts<T extends EchartsInstance, P extends EChartsOption>(
option?: P,
): [(instance: T) => void, EchartsInstance] {
const instanceRef = ref<T>()
function register(instance: T) {
if (isProdMode()) {
if (instance === unref(instanceRef)) {
return
}
onUnmounted(() => {
instanceRef.value = null
})
}
instanceRef.value = instance
option && instance?.setOption(option)
}
function getInstance() {
const instance = unref(instanceRef)
if (!instance) {
console.warn('Echarts instance is undefined!')
}
return instance as T
}
return [
register,
{
setOption: (option: Partial<P>): Promise<void> => {
return new Promise((resolve) => {
tryOnMounted(() => {
getInstance()?.setOption(option)
resolve()
})
})
},
},
]
}
<!-- renderjs 逻辑层 -->
<script>
import { nanoid } from 'nanoid'
export default {
emits: ['register'],
data() {
return {
id: nanoid(),
option: {},
}
},
mounted() {
this.$emit('register', this)
},
methods: {
setOption(option) {
this.option = {
id: this.id,
...option,
}
},
},
}
</script>
<!-- renderjs 视图层模块 -->
<script module="echarts" lang="renderjs" src="./echarts.module.js"></script>
<template>
<!-- #ifdef APP-PLUS || H5 -->
<view class="wrap echarts" :id="id" :option="option" :change:option="echarts.changeOption" />
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<view class="wrap empty">非 APP、H5 环境不支持</view>
<!-- #endif -->
</template>
<style lang="less" scoped>
//
</style>
export function symbolWeatherReplace(data: Recordable) {
return data
}
import { appendScript } from '/@/utils/dom'
/**
* 加载 Echarts 依赖库
*/
export function loadEchartsLibs() {
const id = 'echarts'
const version = '5.4.3'
const resource = `static/js/${id}/${version}/${id}`
return Promise.all([appendScript(id, `${resource}.min.js`)])
}
...@@ -5,7 +5,7 @@ import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM, HandlerUtil } from './index' ...@@ -5,7 +5,7 @@ import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM, HandlerUtil } from './index'
import type { GeoJSONSourceDataUrlParams, HandlerUtilType, MapboxConfig, MapboxInstance } from './index' import type { GeoJSONSourceDataUrlParams, HandlerUtilType, MapboxConfig, MapboxInstance } from './index'
import { API_URL, API_URL_PREFIX } from '/@/utils/net' import { API_URL, API_URL_PREFIX } from '/@/utils/net'
import { isProdMode } from '@/utils/env' import { isProdMode } from '@/utils/env'
import { getToken } from '@/utils/auth' import { useUserStoreWithOut } from '@/store/modules/user'
// 组件名称 // 组件名称
export const name = 'Mapbox' export const name = 'Mapbox'
...@@ -45,6 +45,7 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>( ...@@ -45,6 +45,7 @@ export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>(
return [ return [
register, register,
{ {
setRegion: (region: string) => getInstance()?.setRegion(region),
setConfig: (config: Partial<P>) => getInstance()?.setConfig(config), setConfig: (config: Partial<P>) => getInstance()?.setConfig(config),
isReady: computed(() => instanceRef?.value?.isReady) as unknown as ComputedRef<boolean>, isReady: computed(() => instanceRef?.value?.isReady) as unknown as ComputedRef<boolean>,
...@@ -105,7 +106,7 @@ export function addDefaultSplotLayer( ...@@ -105,7 +106,7 @@ export function addDefaultSplotLayer(
map: MapboxInstance, map: MapboxInstance,
id: string, id: string,
layer?: Partial<mapboxgl.Layer>, layer?: Partial<mapboxgl.Layer>,
beforeId = 'fill-placeholder', beforeId: string = 'fill-placeholder',
) { ) {
map.addLayer( map.addLayer(
merge( merge(
...@@ -139,8 +140,8 @@ export function addDefaultSymbolLayer( ...@@ -139,8 +140,8 @@ export function addDefaultSymbolLayer(
map: MapboxInstance, map: MapboxInstance,
id: string, id: string,
layer?: Partial<mapboxgl.Layer>, layer?: Partial<mapboxgl.Layer>,
beforeId = 'symbol-placeholder', beforeId: string = 'symbol-placeholder',
popop = true, popop: boolean = true,
) { ) {
map.addLayer( map.addLayer(
merge( merge(
...@@ -190,9 +191,10 @@ export function flyTo( ...@@ -190,9 +191,10 @@ export function flyTo(
* @returns source url * @returns source url
*/ */
export function buildGeoJSONSourceDataUrl(config: GeoJSONSourceDataUrlParams | string): string { export function buildGeoJSONSourceDataUrl(config: GeoJSONSourceDataUrlParams | string): string {
const userStore = useUserStoreWithOut()
const baseURL = `${API_URL}${API_URL_PREFIX}` const baseURL = `${API_URL}${API_URL_PREFIX}`
const defaultParams = { const defaultParams = {
Authorization: getToken(), Authorization: userStore.getToken,
} }
const url = typeof config === 'string' ? config : config.url const url = typeof config === 'string' ? config : config.url
......
<script> <script>
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import { useUserStoreWithOut } from '@/store/modules/user'
import { getRegionConfig } from '@/components/Map/Mapbox/regions'
// FIXED: 重要说明 // FIXED: 重要说明
// renderjs 组件暂不支持 setup 组件写法,对 ts 支持也不友好,这里的写法需要参考 vue2 的写法 // renderjs 组件暂不支持 setup 组件写法,对 ts 支持也不友好,这里的写法需要参考 vue2 的写法
...@@ -18,6 +20,7 @@ ...@@ -18,6 +20,7 @@
data() { data() {
return { return {
id: nanoid(), id: nanoid(),
onMapEvent: noop,
onLoadedEvent: noop, onLoadedEvent: noop,
onSourceRequestHandleEvent: noop, onSourceRequestHandleEvent: noop,
onSourceRequestHandleErrorEvent: noop, onSourceRequestHandleErrorEvent: noop,
...@@ -38,7 +41,7 @@ ...@@ -38,7 +41,7 @@
setPaintPropertyOptions: undefined, setPaintPropertyOptions: undefined,
setLayoutPropertyOptions: undefined, setLayoutPropertyOptions: undefined,
flyToOptions: undefined, flyToOptions: undefined,
positionOptions: undefined, regionOptions: undefined,
// change options 锁,结合 event,配合数组实现先进先出队列控制 // change options 锁,结合 event,配合数组实现先进先出队列控制
changeLock: false, changeLock: false,
...@@ -57,27 +60,6 @@ ...@@ -57,27 +60,6 @@
this.$emit('register', this) this.$emit('register', this)
}, },
methods: { methods: {
getCurrentPosition() {
// 获取用户定位
uni.getLocation({
type: 'wgs84',
success: (res) => {
// 更新定位
// 加入到 change 事件队列中
this.changeOptionsQueue.push({
fn: 'position',
coords: res,
timestamp: Date.now(),
})
// 尝试触发 change 事件
this.tryTriggerChange()
},
fail: () => {
Message.toast('获取位置失败,请打开定位权限')
},
})
},
onMapLoad(data) { onMapLoad(data) {
this.loaded = true this.loaded = true
Message.hideLoading() Message.hideLoading()
...@@ -91,8 +73,8 @@ ...@@ -91,8 +73,8 @@
// 触发 onSourceRequestHandle 事件 // 触发 onSourceRequestHandle 事件
this.onSourceRequestHandleEvent?.(data) this.onSourceRequestHandleEvent?.(data)
}, },
onSourceRequestErrorHandle(data) { async onSourceRequestErrorHandle(data) {
console.debug('📢 Request Error Handle', data) console.debug(' Request Error Handle', data)
// 触发 onSourceRequestErrorHandle 事件 // 触发 onSourceRequestErrorHandle 事件
this.onSourceRequestErrorHandleEvent?.(data) this.onSourceRequestErrorHandleEvent?.(data)
...@@ -102,7 +84,8 @@ ...@@ -102,7 +84,8 @@
const { status, message } = JSON.parse(data) const { status, message } = JSON.parse(data)
if (status === 401) { if (status === 401) {
// 跳转到登录页 // 跳转到登录页
uni.reLaunch({ url: '/pages/login/login' }) const userStore = useUserStoreWithOut()
await userStore.logout()
Message.toast('登录信息过期,请重新登录!') Message.toast('登录信息过期,请重新登录!')
} else { } else {
Message.toast(message) Message.toast(message)
...@@ -113,6 +96,12 @@ ...@@ -113,6 +96,12 @@
} }
} }
}, },
// 监听地图事件(on,custom...)
onEventHandle(event) {
console.debug('📢 Map Event', event)
// 触发事件
this.onMapEvent?.(event)
},
// 尝试触发事件, 通过 changeLock 控制 // 尝试触发事件, 通过 changeLock 控制
tryTriggerChange() { tryTriggerChange() {
if (!this.changeLock && this.changeOptionsQueue.length) { if (!this.changeLock && this.changeOptionsQueue.length) {
...@@ -123,19 +112,50 @@ ...@@ -123,19 +112,50 @@
}, },
// 监听地图配置从逻辑层到视图层的响应式变化事件 // 监听地图配置从逻辑层到视图层的响应式变化事件
onMapOptionsChangeEvent(event) { onMapOptionsChangeEvent(event) {
console.debug('🔊 Map Event', event) console.debug('🔊 Map Options Change Event', event)
this.changeLock = false this.changeLock = false
this.tryTriggerChange() this.tryTriggerChange()
}, },
/**
* 解析区域编码获得区域配置
* @param {string} code 区域编码
*/
analysisRegion(code) {
const region = code || '43'
const regionConfig = getRegionConfig(region)
return {
region,
regionConfig,
}
},
// 设置地图区域编码,用于覆盖遮罩层
setRegion(value) {
const { region, regionConfig } = this.analysisRegion(value)
// 加入到 change 事件队列中
this.changeOptionsQueue.push({
fn: 'region',
region,
regionConfig,
})
// 尝试触发 change 事件
this.tryTriggerChange()
},
// 设置地图配置
setConfig(config) { setConfig(config) {
const { region, regionConfig } = this.analysisRegion(config?.region)
this.options = { this.options = {
container: this.id, container: this.id,
style: config?.style, style: config?.style,
options: config?.options, options: config?.options,
attribution: config?.attribution, attribution: config?.attribution,
extra: config?.extra,
region,
regionConfig,
} }
this.onMapEvent = config?.onMapEvent || noop
this.onLoadedEvent = config?.onLoaded || noop this.onLoadedEvent = config?.onLoaded || noop
this.onSourceRequestHandleEvent = config?.onSourceRequestHandle || noop this.onSourceRequestHandleEvent = config?.onSourceRequestHandle || noop
this.onSourceRequestErrorHandleEvent = config?.onSourceRequestErrorHandle || noop this.onSourceRequestErrorHandleEvent = config?.onSourceRequestErrorHandle || noop
...@@ -319,6 +339,24 @@ ...@@ -319,6 +339,24 @@
// 尝试触发 change 事件 // 尝试触发 change 事件
this.tryTriggerChange() this.tryTriggerChange()
}, },
// 获取当前位置
getCurrentPosition() {
// 获取用户定位
uni.getLocation({
type: 'wgs84',
success: (res) => {
// 更新定位
this.position = {
coords: res,
_: Date.now(),
}
},
fail: () => {
Message.toast('获取位置失败,请打开定位权限')
},
})
},
}, },
} }
</script> </script>
...@@ -355,8 +393,8 @@ ...@@ -355,8 +393,8 @@
:change:removeOptions="mapbox.changeRemoveOptions" :change:removeOptions="mapbox.changeRemoveOptions"
:flyToOptions="flyToOptions" :flyToOptions="flyToOptions"
:change:flyToOptions="mapbox.changeFlyToOptions" :change:flyToOptions="mapbox.changeFlyToOptions"
:positionOptions="positionOptions" :regionOptions="regionOptions"
:change:positionOptions="mapbox.changePositionOptions" :change:regionOptions="mapbox.changeRegionOptions"
/> />
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-PLUS || H5 --> <!-- #ifndef APP-PLUS || H5 -->
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
// JSON 数据 // JSON 数据
option: { option: {
type: Object as PropType<Option>, type: Object as PropType<Option>,
default: () => ({}), default: () => ({}) as Option,
}, },
}) })
...@@ -80,6 +80,20 @@ ...@@ -80,6 +80,20 @@
toggleShow, toggleShow,
toggleExpand, toggleExpand,
}) })
function getBorderColor(color: string) {
switch (color) {
case 'white':
case '#fff':
case '#ffffff':
case '#FFFFFF':
case 'rgb(255, 255, 255)':
case 'rgba(255, 255, 255, 1)':
return '#d7d7d7'
default:
return color
}
}
</script> </script>
<template> <template>
...@@ -107,7 +121,11 @@ ...@@ -107,7 +121,11 @@
<view <view
v-if="item.color" v-if="item.color"
class="color" class="color"
:style="{ backgroundColor: item.color, ...data.option.blockStyle }" :style="{
backgroundColor: item.color,
border: `1rpx solid ${getBorderColor(item.color)}`,
...data.option.blockStyle,
}"
/> />
<!-- 标签 --> <!-- 标签 -->
...@@ -209,9 +227,9 @@ ...@@ -209,9 +227,9 @@
} }
.color { .color {
width: 38rpx; width: 42rpx;
height: 24rpx; height: 24rpx;
margin-right: 8rpx; margin-right: 10rpx !important;
} }
.label { .label {
......
...@@ -13,7 +13,7 @@ export interface OptionItem { ...@@ -13,7 +13,7 @@ export interface OptionItem {
export interface Option { export interface Option {
// 选项 // 选项
items: OptionItem[] | OptionItem[][] items?: OptionItem[] | OptionItem[][]
// label 样式 // label 样式
labelStyle?: Recordable labelStyle?: Recordable
// 色块样式 // 色块样式
......
...@@ -23,10 +23,12 @@ export function useTimeBarWidget<T extends TimeBarInstance, P extends TimeBarPro ...@@ -23,10 +23,12 @@ export function useTimeBarWidget<T extends TimeBarInstance, P extends TimeBarPro
{ {
...fns, ...fns,
getTime: () => get()?.getTime(), getTime: () => get()?.getTime(),
setTime: (time: Dayjs[]) => get()?.setTime(time), setTime: (time: Dayjs[], mode: boolean) => get()?.setTime(time, mode),
getCheckedOption: () => get()?.getCheckedOption(), getCheckedOption: () => get()?.getCheckedOption(),
setCheckedOption: (index: number) => get()?.setCheckedOption(index), setCheckedOption: (index: number) => get()?.setCheckedOption(index),
getTimeBarValue: () => get()?.getTimeBarValue(), getTimeBarValue: () => get()?.getTimeBarValue(),
setHourRange: (hourRange: Array<string | number> | string) => get()?.setHourRange(hourRange),
setMinuteRange: (minuteRange: Array<string | number> | string) => get()?.setMinuteRange(minuteRange),
}, },
] ]
} }
......
...@@ -32,6 +32,8 @@ export interface TimeBarTime { ...@@ -32,6 +32,8 @@ export interface TimeBarTime {
format?: string format?: string
value?: Dayjs[] value?: Dayjs[]
onChange?: (e: TimeBarChangeEvent) => void onChange?: (e: TimeBarChangeEvent) => void
hourRange?: Array<number | string> | string
minuteRange?: Array<number | string> | string
} }
export interface TimeBarButton { export interface TimeBarButton {
...@@ -87,7 +89,7 @@ export interface TimeBarInstance extends BasicWidgetInstance<TimeBarProps> { ...@@ -87,7 +89,7 @@ export interface TimeBarInstance extends BasicWidgetInstance<TimeBarProps> {
* 设置时间 * 设置时间
* @param time 时间 * @param time 时间
*/ */
setTime: (time: Dayjs[]) => void setTime: (time: Dayjs[], mode?: boolean) => void
/** /**
* 获取选中的选项 * 获取选中的选项
* @returns 选中的选项 * @returns 选中的选项
...@@ -103,4 +105,17 @@ export interface TimeBarInstance extends BasicWidgetInstance<TimeBarProps> { ...@@ -103,4 +105,17 @@ export interface TimeBarInstance extends BasicWidgetInstance<TimeBarProps> {
* @returns 时间栏的值 * @returns 时间栏的值
*/ */
getTimeBarValue: () => TimeBarChangeEvent getTimeBarValue: () => TimeBarChangeEvent
/**
* 设置小时可选范围
* @param hourRange
* @returns
*/
setHourRange: (hourRange: Array<number | string> | string) => void
/**
* 设置分钟可选范围
* @param minuteRange
* @returns
*/
setMinuteRange: (minuteRange: Array<number | string> | string) => void
} }
...@@ -88,6 +88,7 @@ ...@@ -88,6 +88,7 @@
// 打开 Select 组件 // 打开 Select 组件
model.selectPopup.title = button.name model.selectPopup.title = button.name
model.selectPopup.options = button.options ?? [] model.selectPopup.options = button.options ?? []
model.selectPopup.checked = button.options?.filter((item) => item.checked).map((item) => item.value) ?? []
model.selectPopup.multiple = button.multiple ?? false model.selectPopup.multiple = button.multiple ?? false
model.selectPopup.show = true model.selectPopup.show = true
} else if (button.type === 'filter') { } else if (button.type === 'filter') {
...@@ -108,13 +109,26 @@ ...@@ -108,13 +109,26 @@
multiple: false, multiple: false,
title: '', title: '',
options: [], options: [],
onConfirm: (e: Recordable) => { checked: [],
onConfirm: (item: Recordable, close?: boolean) => {
if (model.selectPopup.multiple) {
item.checked = !item.checked
} else {
model.selectPopup.options.forEach((option) => (option.checked = false))
item.checked = true
}
model.activeButton?.handle({ model.activeButton?.handle({
type: 'change', type: 'change',
name: model.activeButton.name, name: model.activeButton.name,
value: toRaw(e.options), value: toRaw(
model.selectPopup.options[model.selectPopup.multiple ? 'filter' : 'find'](
(item) => item.checked,
),
),
}) })
model.selectPopup.onClose()
close && model.selectPopup.onClose()
}, },
onClose: () => (model.selectPopup.show = false), onClose: () => (model.selectPopup.show = false),
}, },
...@@ -191,22 +205,79 @@ ...@@ -191,22 +205,79 @@
<!-- 交互组件 --> <!-- 交互组件 -->
<!-- 1. Select Popup --> <!-- 1. Select Popup -->
<fui-select <!-- <fui-select
maskClosable maskClosable
:type="model.selectPopup.multiple ? 'select' : 'list'"
:show="model.selectPopup.show" :show="model.selectPopup.show"
:title="`选择${model.selectPopup.title}`" :title="`选择${model.selectPopup.title}`"
:options="model.selectPopup.options" :options="model.selectPopup.options"
:multiple="model.selectPopup.multiple" :multiple="model.selectPopup.multiple"
@click="model.selectPopup.onConfirm"
@confirm="model.selectPopup.onConfirm" @confirm="model.selectPopup.onConfirm"
@close="model.selectPopup.onClose" @close="model.selectPopup.onClose"
/> /> -->
<fui-bottom-popup
:show="model.selectPopup.show"
:z-index="900"
@close="model.selectPopup.onClose"
:safeArea="false"
>
<view class="popup-wrap">
<view class="fui-title">{{ `选择${model.selectPopup.title}` }}</view>
<view class="fui-icon__close" @tap="model.selectPopup.onClose">
<fui-icon name="close" :size="48" />
</view>
<scroll-view scroll-y class="fui-scroll__view">
<view class="fui-custom__wrap">
<!-- 多选 -->
<template v-if="model.selectPopup.multiple">
<fui-checkbox-group>
<fui-label
v-for="(item, index) in model.selectPopup.options"
:key="index"
@tap="model.selectPopup.onConfirm(item, false)"
>
<fui-list-cell>
<view class="fui-align__center">
<fui-checkbox v-model:checked="item.checked" :value="item.value" />
<text class="fui-text pl-2">{{ item.text }}</text>
</view>
</fui-list-cell>
</fui-label>
</fui-checkbox-group>
</template>
<!-- 单选 -->
<template v-else>
<!-- -->
<fui-radio-group>
<fui-label
v-for="(item, index) in model.selectPopup.options"
:key="index"
@tap="model.selectPopup.onConfirm(item, true)"
>
<fui-list-cell>
<view class="fui-align__center">
<fui-radio :checked="item.checked" :value="item.value" />
<text class="fui-text pl-2">{{ item.text }}</text>
</view>
</fui-list-cell>
</fui-label>
</fui-radio-group>
</template>
</view>
</scroll-view>
</view>
<!-- 底部安全区 -->
<fui-safe-area />
</fui-bottom-popup>
<!-- 2. 过滤 Popup --> <!-- 2. 过滤 Popup -->
<fui-bottom-popup <fui-bottom-popup
:show="model.filterPopup.show" :show="model.filterPopup.show"
:z-index="900" :z-index="900"
@close="model.filterPopup.onClose" @close="model.filterPopup.onClose"
:safeArea="true" :safeArea="false"
> >
<view class="popup-wrap"> <view class="popup-wrap">
<view class="fui-title">{{ model.filterPopup.title }}</view> <view class="fui-title">{{ model.filterPopup.title }}</view>
...@@ -268,6 +339,9 @@ ...@@ -268,6 +339,9 @@
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<!-- 底部安全区 -->
<fui-safe-area />
</fui-bottom-popup> </fui-bottom-popup>
</view> </view>
</template> </template>
...@@ -358,7 +432,7 @@ ...@@ -358,7 +432,7 @@
} }
.popup-wrap { .popup-wrap {
height: 500rpx; height: 520rpx;
padding-top: 30rpx; padding-top: 30rpx;
position: relative; position: relative;
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -170,7 +170,32 @@ ...@@ -170,7 +170,32 @@
.mapboxgl-popup-content .popup-row { .mapboxgl-popup-content .popup-row {
display: flex; display: flex;
align-items: flex-start;
}
.mapboxgl-popup-content .popup-row.timing {
justify-content: center;
padding-top: 6px;
}
.mapboxgl-popup-content .popup-row.timing button {
background: #1890ff;
border: none;
padding: 4px 8px;
color: #fff;
border-radius: 5px;
font-size: 14px;
display: flex;
align-items: center; align-items: center;
justify-content: center;
}
.mapboxgl-popup-content .popup-row.timing button .icon {
width: 14px;
height: 14px;
margin-right: 5px;
display: inline-block;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik00IDloNHYxMUg0em0xMiA0aDR2N2gtNHptLTYtOWg0djE2aC00eiIvPjwvc3ZnPg==');
} }
.mapboxgl-popup-content .popup-chart-icon { .mapboxgl-popup-content .popup-chart-icon {
...@@ -182,6 +207,7 @@ ...@@ -182,6 +207,7 @@
.mapboxgl-popup-content .popup-title { .mapboxgl-popup-content .popup-title {
font-weight: bold; font-weight: bold;
min-width: 3em;
} }
.mapboxgl-popup-content .popup-title sub { .mapboxgl-popup-content .popup-title sub {
......
export function appendStylesheet(id: string, href: string) { export function appendStylesheet(id: string, href: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const element = document.getElementById(id) as HTMLLinkElement
if (element && element.href === href) {
resolve(0)
return
}
const link = document.createElement('link') const link = document.createElement('link')
link.id = id link.id = id
link.rel = 'stylesheet' link.rel = 'stylesheet'
...@@ -12,9 +18,16 @@ export function appendStylesheet(id: string, href: string) { ...@@ -12,9 +18,16 @@ export function appendStylesheet(id: string, href: string) {
export function appendScript(id: string, src: string) { export function appendScript(id: string, src: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const element = document.getElementById(id) as HTMLScriptElement
if (element && element.src === src) {
resolve(0)
return
}
const script = document.createElement('script') const script = document.createElement('script')
script.id = id script.id = id
script.src = src script.src = src
script.onload = resolve script.onload = resolve
script.onerror = script.onabort = reject script.onerror = script.onabort = reject
document.head.appendChild(script) document.head.appendChild(script)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论