提交 13efb5af 作者: 吴佳伟

feat: 农场页面天气工具

上级 dfadc655
import { weatherHttp } from '/@/utils/http/axios' import { otherHttp, weatherHttp } from '/@/utils/http/axios'
const API_KEY = '4fb5c9e814994516b6522419651f2e9e' const API_KEY = '4fb5c9e814994516b6522419651f2e9e'
...@@ -22,3 +22,25 @@ export function alarm(location: string) { ...@@ -22,3 +22,25 @@ export function alarm(location: string) {
url: `/weatheralert/v1/current/${location.replace(',', '/')}?key=${API_KEY}`, url: `/weatheralert/v1/current/${location.replace(',', '/')}?key=${API_KEY}`,
}) })
} }
// 格点天气预报-日值(降雨、大风用)
export function gridForecastDaily(location: string, day = 3) {
return weatherHttp.get({
url: `/v7/weather/${day}d?key=${API_KEY}&location=${location}`,
})
}
// 格点天气预报-小时值(温度用)
export function gridForecastHourly(location: string) {
return weatherHttp.get({
url: `/v7/weather/24h?key=${API_KEY}&location=${location}`,
})
}
// 强对流预报(自建接口)
export function severeForecast(lon: string, lat: string) {
return otherHttp.get({
url: '/weather/forecast',
params: { lon, lat },
}, { isTransformResponse: false })
}
...@@ -13,17 +13,17 @@ export default { ...@@ -13,17 +13,17 @@ export default {
}, },
methods: { methods: {
loadLibs: loadEchartsLibs, loadLibs: loadEchartsLibs,
init() { init(notMerge) {
// 如果已经初始化过了,就直接更新配置项 // 如果已经初始化过了,就直接更新配置项
if (this.chart && this.chart.setOption) { if (this.chart && this.chart.setOption) {
this.chart.setOption(this.option) this.chart.setOption(this.option, { notMerge: notMerge || false })
return return
} }
// 初始化组件 // 初始化组件
const chart = window.echarts.init(document.getElementById(this.option.id)) const chart = window.echarts.init(document.getElementById(this.option.id))
// 设置图表配置项 // 设置图表配置项
chart.setOption(this.option) chart.setOption(this.option, { notMerge: notMerge || false })
this.chart = chart this.chart = chart
}, },
changeOption(option) { changeOption(option) {
...@@ -45,12 +45,20 @@ export default { ...@@ -45,12 +45,20 @@ export default {
} }
} }
const notMerge = option.__notMerge
// 清除内部标记
delete option.__notMerge
if (notMerge) {
this.option = option
} else {
this.option = merge(this.option, option) this.option = merge(this.option, option)
}
if (typeof window.echarts === 'object' && typeof window.echarts.init === 'function') { if (typeof window.echarts === 'object' && typeof window.echarts.init === 'function') {
this.init() this.init(notMerge)
} else { } else {
this.loadLibs().then(() => { this.loadLibs().then(() => {
this.init() this.init(notMerge)
}) })
} }
}, },
......
...@@ -44,10 +44,10 @@ export function useEcharts<T extends EchartsInstance, P extends EChartsOption>( ...@@ -44,10 +44,10 @@ export function useEcharts<T extends EchartsInstance, P extends EChartsOption>(
return [ return [
register, register,
{ {
setOption: (option: Partial<P>): Promise<void> => { setOption: (option: Partial<P>, opts?: { notMerge?: boolean; lazyUpdate?: boolean }): Promise<void> => {
return new Promise((resolve) => { return new Promise((resolve) => {
tryOnMounted(() => { tryOnMounted(() => {
getInstance()?.setOption(option) getInstance()?.setOption(option, opts)
resolve() resolve()
}) })
}) })
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
this.$emit('register', this) this.$emit('register', this)
}, },
methods: { methods: {
setOption(option) { setOption(option, opts) {
// 处理tooltip formatter函数序列化 // 处理tooltip formatter函数序列化
if (option.tooltip && typeof option.tooltip.formatter === 'function') { if (option.tooltip && typeof option.tooltip.formatter === 'function') {
// 将函数转换为字符串,在视图层重新构建 // 将函数转换为字符串,在视图层重新构建
...@@ -25,6 +25,11 @@ ...@@ -25,6 +25,11 @@
id: this.id, id: this.id,
...option, ...option,
} }
// 保存 opts 供 renderjs 层使用
if (opts?.notMerge !== undefined) {
this.option.__notMerge = opts.notMerge
}
}, },
}, },
} }
......
<!-- 天气折线图弹窗 -->
<script setup lang="ts">
import dayjs from 'dayjs'
import { Echarts, useEcharts } from '@/components/Echarts'
import * as WeatherAPI from '@/api/model/weather'
const props = defineProps<{
visible: boolean
type: 'rain' | 'temp' | 'wind' | 'severe'
lon: string | number
lat: string | number
}>()
const emit = defineEmits(['close'])
const [register, chart] = useEcharts()
// 标题映射
const titleMap = {
rain: '降雨预报',
temp: '温度预报',
wind: '大风预报',
severe: '强对流预报',
}
// Y 轴单位映射
const unitMap = {
rain: 'mm',
temp: '°C',
wind: 'km/h',
severe: '',
}
// 加载状态
const loading = ref(false)
const errorMsg = ref('')
const updateText = ref('')
// 请求序号,用于取消旧请求
let requestSeq = 0
// 监听 visible/type/lon/lat 变化,拉取数据
watch(
() => [props.visible, props.type, props.lon, props.lat] as const,
async ([visible]) => {
if (!visible) {
return
}
if (!props.lon || !props.lat) {
errorMsg.value = '未设置农场坐标位置'
return
}
await fetchWeatherData()
},
{ immediate: true },
)
async function fetchWeatherData() {
const seq = ++requestSeq
loading.value = true
errorMsg.value = ''
try {
let data: any
switch (props.type) {
case 'rain':
data = await WeatherAPI.gridForecastDaily(`${props.lon},${props.lat}`, 7)
if (seq !== requestSeq) return
await nextTick()
renderRainChart(data.data)
break
case 'temp':
data = await WeatherAPI.gridForecastDaily(`${props.lon},${props.lat}`, 7)
if (seq !== requestSeq) return
await nextTick()
renderTempChart(data.data)
break
case 'wind':
data = await WeatherAPI.gridForecastDaily(`${props.lon},${props.lat}`, 7)
if (seq !== requestSeq) return
await nextTick()
renderWindChart(data.data)
break
case 'severe':
data = await WeatherAPI.severeForecast(String(props.lon), String(props.lat))
if (seq !== requestSeq) return
await nextTick()
renderSevereChart(data)
break
}
updateText.value = `数据更新时间: ${dayjs().format('YYYY-MM-DD HH:mm')}`
} catch (e: any) {
console.error('获取天气数据失败:', e)
// 如果在此期间有新请求,不覆盖其错误状态
if (seq !== requestSeq) {
return
}
errorMsg.value = '数据加载失败'
} finally {
loading.value = false
}
}
// 渲染空图表
function renderEmptyChart(hint?: string) {
chart.setOption(
{
title: { show: false },
tooltip: { show: false },
grid: { top: 60, right: 30, bottom: 40, left: 60 },
xAxis: { type: 'category', data: [], show: false },
yAxis: { type: 'value', show: false },
series: [],
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: hint || '暂无数据',
fontSize: 16,
fill: '#bbb',
},
},
},
{ notMerge: true },
)
}
function renderRainChart(data: any) {
if (!data?.daily || !Array.isArray(data.daily)) {
renderEmptyChart('暂无预报数据')
return
}
const dates = data.daily.map((d: any) => dayjs(d.fxDate).format('MM-DD'))
const precip = data.daily.map((d: any) => Number.parseFloat(d.precip) || 0)
chart.setOption(
{
title: { show: false },
tooltip: { trigger: 'axis' },
legend: {
top: 0,
textStyle: { fontSize: 14, color: '#333' },
},
grid: { top: 60, right: 30, bottom: 40, left: 60 },
xAxis: {
type: 'category',
data: dates,
axisLabel: { fontSize: 14, color: '#bbb', margin: 10 },
axisLine: { lineStyle: { color: '#eee' } },
axisTick: { show: false },
},
yAxis: {
type: 'value',
name: '(mm)',
nameTextStyle: { fontSize: 14, color: '#bbb', padding: [0, 0, 0, -10] },
axisLabel: { fontSize: 14, color: '#bbb' },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { lineStyle: { type: 'dashed', color: '#eee', width: 1 } },
},
series: [
{
name: '降水量',
type: 'line',
data: precip,
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: '#6db876' },
label: {
show: true,
position: 'top',
fontSize: 12,
color: '#666',
},
lineStyle: { color: '#6db876', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(109,184,118,0.3)' },
{ offset: 1, color: 'rgba(109,184,118,0)' },
],
},
},
},
],
},
{ notMerge: true },
)
}
function renderTempChart(data: any) {
if (!data?.daily || !Array.isArray(data.daily)) {
renderEmptyChart('暂无预报数据')
return
}
const dates = data.daily.map((d: any) => dayjs(d.fxDate).format('MM-DD'))
const tempMax = data.daily.map((d: any) => Number.parseFloat(d.tempMax) || 0)
const tempMin = data.daily.map((d: any) => Number.parseFloat(d.tempMin) || 0)
chart.setOption(
{
title: { show: false },
tooltip: { trigger: 'axis' },
legend: {
top: 0,
textStyle: { fontSize: 14, color: '#333' },
},
grid: { top: 60, right: 30, bottom: 40, left: 60 },
xAxis: {
type: 'category',
data: dates,
axisLabel: { fontSize: 14, color: '#bbb', margin: 10 },
axisLine: { lineStyle: { color: '#eee' } },
axisTick: { show: false },
},
yAxis: {
type: 'value',
name: '(°C)',
nameTextStyle: { fontSize: 14, color: '#bbb', padding: [0, 0, 0, -10] },
axisLabel: { fontSize: 14, color: '#bbb' },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { lineStyle: { type: 'dashed', color: '#eee', width: 1 } },
},
series: [
{
name: '最高温度',
type: 'line',
data: tempMax,
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: '#e74c3c' },
label: {
show: true,
position: 'top',
fontSize: 12,
color: '#666',
},
lineStyle: { color: '#e74c3c', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(231,76,60,0.3)' },
{ offset: 1, color: 'rgba(231,76,60,0)' },
],
},
},
},
{
name: '最低温度',
type: 'line',
data: tempMin,
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: '#3498db' },
label: {
show: true,
position: 'bottom',
fontSize: 12,
color: '#666',
},
lineStyle: { color: '#3498db', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(52,152,219,0.2)' },
{ offset: 1, color: 'rgba(52,152,219,0)' },
],
},
},
},
],
},
{ notMerge: true },
)
}
function renderWindChart(data: any) {
if (!data?.daily || !Array.isArray(data.daily)) {
renderEmptyChart('暂无预报数据')
return
}
const dates = data.daily.map((d: any) => dayjs(d.fxDate).format('MM-DD'))
const windDay = data.daily.map((d: any) => Number.parseFloat(d.windSpeedDay) || 0)
const windNight = data.daily.map((d: any) => Number.parseFloat(d.windSpeedNight) || 0)
chart.setOption(
{
title: { show: false },
tooltip: { trigger: 'axis' },
legend: {
top: 0,
textStyle: { fontSize: 14, color: '#333' },
},
grid: { top: 60, right: 30, bottom: 40, left: 60 },
xAxis: {
type: 'category',
data: dates,
axisLabel: { fontSize: 14, color: '#bbb', margin: 10 },
axisLine: { lineStyle: { color: '#eee' } },
axisTick: { show: false },
},
yAxis: {
type: 'value',
name: '(km/h)',
nameTextStyle: { fontSize: 14, color: '#bbb', padding: [0, 0, 0, -10] },
axisLabel: { fontSize: 14, color: '#bbb' },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { lineStyle: { type: 'dashed', color: '#eee', width: 1 } },
},
series: [
{
name: '白天风速',
type: 'line',
data: windDay,
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: '#e67e22' },
label: {
show: true,
position: 'top',
fontSize: 12,
color: '#666',
},
lineStyle: { color: '#e67e22', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(230,126,34,0.3)' },
{ offset: 1, color: 'rgba(230,126,34,0)' },
],
},
},
},
{
name: '夜间风速',
type: 'line',
data: windNight,
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: '#3498db' },
label: {
show: true,
position: 'bottom',
fontSize: 12,
color: '#666',
},
lineStyle: { color: '#3498db', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(52,152,219,0.2)' },
{ offset: 1, color: 'rgba(52,152,219,0)' },
],
},
},
},
],
},
{ notMerge: true },
)
}
function renderSevereChart(data: any) {
if (!data?.elements || !Array.isArray(data.elements) || data.elements.length === 0) {
renderEmptyChart('暂无预报数据')
return
}
const elements = data.elements
// 从第一个要素提取时间轴
const firstElement = elements[0]
if (
!firstElement?.timeDataList ||
!Array.isArray(firstElement.timeDataList) ||
firstElement.timeDataList.length === 0
) {
renderEmptyChart('暂无预报数据')
return
}
const times = firstElement.timeDataList.map((d: any) => dayjs(d.forecastTime).format('HH:mm'))
const colors = ['#e74c3c', '#3498db', '#f39c12']
const series = elements.map((el: any, idx: number) => ({
name: el.name,
type: 'line',
data: el.timeDataList.map((d: any) => d.value || 0),
smooth: true,
showSymbol: true,
symbolSize: 6,
itemStyle: { color: colors[idx % colors.length] },
label: {
show: true,
position: 'top',
fontSize: 12,
color: '#666',
},
lineStyle: { color: colors[idx % colors.length], width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: `${colors[idx % colors.length]}4D` },
{ offset: 1, color: `${colors[idx % colors.length]}00` },
],
},
},
}))
// Y 轴单位:取第一个要素的单位
const unit = elements[0]?.timeDataList?.[0]?.unit || ''
chart.setOption(
{
title: { show: false },
tooltip: { trigger: 'axis' },
legend: {
top: 0,
textStyle: { fontSize: 14, color: '#333' },
},
grid: { top: 60, right: 30, bottom: 40, left: 60 },
xAxis: {
type: 'category',
data: times,
axisLabel: { fontSize: 14, color: '#bbb', margin: 10 },
axisLine: { lineStyle: { color: '#eee' } },
axisTick: { show: false },
},
yAxis: {
type: 'value',
name: `(${unit})`,
nameTextStyle: { fontSize: 14, color: '#bbb', padding: [0, 0, 0, -10] },
axisLabel: { fontSize: 14, color: '#bbb' },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { lineStyle: { type: 'dashed', color: '#eee', width: 1 } },
},
series,
},
{ notMerge: true },
)
}
</script>
<template>
<view v-show="visible" class="weather-chart-overlay" @tap="emit('close')">
<view class="chart-popup" @tap.stop>
<!-- 标题栏 -->
<view class="popup-header">
<text class="popup-title">{{ titleMap[type] }}</text>
<view class="close-btn" @tap="emit('close')">
<text class="close-icon">×</text>
</view>
</view>
<!-- 加载/错误状态 -->
<view v-if="loading" class="chart-status">
<text>加载中...</text>
</view>
<view v-else-if="errorMsg" class="chart-status error">
<text>{{ errorMsg }}</text>
<view class="retry-btn" @tap="fetchWeatherData">
<text>重试</text>
</view>
</view>
<!-- 图表区域 -->
<view v-show="!loading && !errorMsg" class="chart-container">
<Echarts @register="register" class="chart-area" />
</view>
<!-- 更新时间 -->
<view v-if="updateText && !loading && !errorMsg" class="update-info">
<text class="update-text">{{ updateText }}</text>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.weather-chart-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 998;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.3);
}
.chart-popup {
width: 90%;
max-width: 600px;
background: linear-gradient(180deg, rgba(200, 255, 200, 1) 0%, rgba(255, 255, 255, 1) 40%);
border-radius: 32rpx;
position: relative;
overflow: hidden;
transform: translateY(-40%);
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 30rpx 10rpx;
.popup-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.close-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.6;
}
.close-icon {
font-size: 36rpx;
color: #ccc;
font-weight: 300;
}
}
}
.chart-status {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400rpx;
font-size: 28rpx;
color: #999;
&.error {
color: #e74c3c;
}
}
.retry-btn {
margin-top: 20rpx;
padding: 16rpx 40rpx;
background-color: #5db66f;
color: #fff;
border-radius: 32rpx;
font-size: 26rpx;
&:active {
opacity: 0.8;
}
}
.chart-container {
width: 100%;
height: 350rpx;
}
.chart-area {
width: 100%;
height: 100%;
}
.update-info {
padding: 0 30rpx 20rpx;
text-align: center;
.update-text {
font-size: 20rpx;
color: #bbb;
}
}
</style>
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
import { ToolBoxWidget, useToolBoxWidget } from '@/components/Map/Widgets/ToolBox' import { ToolBoxWidget, useToolBoxWidget } from '@/components/Map/Widgets/ToolBox'
import * as NongchangAPI from '@/api/model/nongchang' import * as NongchangAPI from '@/api/model/nongchang'
import * as farmbaseApi from '@/api/model/farmbase' import * as farmbaseApi from '@/api/model/farmbase'
import WeatherChartPopup from './components/WeatherChartPopup.vue'
// 页面参数 // 页面参数
const page = reactive<Page>({ const page = reactive<Page>({
...@@ -64,7 +65,11 @@ ...@@ -64,7 +65,11 @@
farmbaseInfo: {}, farmbaseInfo: {},
farmbaseInfoList: [], farmbaseInfoList: [],
deviceTypeCount: [], deviceTypeCount: [],
activeWeatherType: '' as '' | 'rain' | 'temp' | 'wind' | 'severe',
}) })
// 当前基地中心经纬度(用于天气接口请求)
const baseCenterLonLat = ref<[number, number] | null>(null)
onLoad(() => { onLoad(() => {
const today = dayjs() const today = dayjs()
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
...@@ -113,6 +118,8 @@ ...@@ -113,6 +118,8 @@
if (c[1] > maxLat) maxLat = c[1] if (c[1] > maxLat) maxLat = c[1]
} }
const center: [number, number] = [(minLon + maxLon) / 2, (minLat + maxLat) / 2] const center: [number, number] = [(minLon + maxLon) / 2, (minLat + maxLat) / 2]
// 存储基地中心经纬度,供天气接口使用
baseCenterLonLat.value = center
// 使用 flyTo 而非 fitBounds,避免 maxBounds 约束干扰 // 使用 flyTo 而非 fitBounds,避免 maxBounds 约束干扰
map.flyTo({ map.flyTo({
center, center,
...@@ -121,8 +128,10 @@ ...@@ -121,8 +128,10 @@
}) })
} }
} else if (res.longitude && res.latitude) { } else if (res.longitude && res.latitude) {
const center: [number, number] = [Number(res.longitude), Number(res.latitude)]
baseCenterLonLat.value = center
map.flyTo({ map.flyTo({
center: [Number(res.longitude), Number(res.latitude)], center,
zoom: 15, zoom: 15,
duration: 1000, duration: 1000,
}) })
...@@ -347,15 +356,19 @@ ...@@ -347,15 +356,19 @@
if (c[1] < minLat) minLat = c[1] if (c[1] < minLat) minLat = c[1]
if (c[1] > maxLat) maxLat = c[1] if (c[1] > maxLat) maxLat = c[1]
} }
const center: [number, number] = [(minLon + maxLon) / 2, (minLat + maxLat) / 2]
baseCenterLonLat.value = center
map.flyTo({ map.flyTo({
center: [(minLon + maxLon) / 2, (minLat + maxLat) / 2], center,
zoom: 15, zoom: 15,
duration: 0, duration: 0,
}) })
} }
} else if (firstBase.longitude && firstBase.latitude) { } else if (firstBase.longitude && firstBase.latitude) {
const center: [number, number] = [Number(firstBase.longitude), Number(firstBase.latitude)]
baseCenterLonLat.value = center
map.flyTo({ map.flyTo({
center: [Number(firstBase.longitude), Number(firstBase.latitude)], center,
zoom: 15, zoom: 15,
duration: 0, duration: 0,
}) })
...@@ -454,33 +467,33 @@ ...@@ -454,33 +467,33 @@
{ {
name: '降雨', name: '降雨',
icon: '/static/images/codefun/rain.png', icon: '/static/images/codefun/rain.png',
type: 'button', type: 'toggle' as const,
handle: () => { handle: () => {
Message.toast('暂无降雨数据') onWeatherClick('rain')
}, },
}, },
{ {
name: '温度', name: '温度',
icon: '/static/images/codefun/temp.png', icon: '/static/images/codefun/temp.png',
type: 'button', type: 'toggle' as const,
handle: () => { handle: () => {
Message.toast('暂无温度数据') onWeatherClick('temp')
}, },
}, },
{ {
name: '强对流', name: '强对流',
icon: '/static/images/codefun/severe.png', icon: '/static/images/codefun/severe.png',
type: 'button', type: 'toggle' as const,
handle: () => { handle: () => {
Message.toast('暂无强对流数据') onWeatherClick('severe')
}, },
}, },
{ {
name: '大风', name: '大风',
icon: '/static/images/codefun/wind.png', icon: '/static/images/codefun/wind.png',
type: 'button', type: 'toggle' as const,
handle: () => { handle: () => {
Message.toast('暂无大风数据') onWeatherClick('wind')
}, },
}, },
{ {
...@@ -522,6 +535,14 @@ ...@@ -522,6 +535,14 @@
}, },
}) })
} }
// 天气按钮点击处理
function onWeatherClick(type: 'rain' | 'temp' | 'wind' | 'severe') {
if (model.activeWeatherType === type) {
model.activeWeatherType = ''
} else {
model.activeWeatherType = type
}
}
// 保留原有的导航栏按钮点击回调(如果有需要的话) // 保留原有的导航栏按钮点击回调(如果有需要的话)
onNavigationBarButtonTap((e) => { onNavigationBarButtonTap((e) => {
if (e.index === 0) { if (e.index === 0) {
...@@ -736,6 +757,13 @@ ...@@ -736,6 +757,13 @@
@submit-success="getDeviceTypeCount" @submit-success="getDeviceTypeCount"
@close="showDialog = false" @close="showDialog = false"
/> />
<WeatherChartPopup
:visible="model.activeWeatherType !== ''"
:type="model.activeWeatherType"
:lon="baseCenterLonLat?.[0] ?? ''"
:lat="baseCenterLonLat?.[1] ?? ''"
@close="model.activeWeatherType = ''"
/>
</view> </view>
</template> </template>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论