feat: 优化页面效果
This commit is contained in:
parent
1c4633881d
commit
c79c868c41
@ -536,6 +536,244 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
min-width: 24px;
|
min-width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.weather-forecast-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 55%;
|
||||||
|
right: 20px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 280px;
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
z-index: 1100;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
transform: translate(100%, -50%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2d3748;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #718096;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-close-btn:hover {
|
||||||
|
background: rgba(116, 129, 141, 0.1);
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-device-info {
|
||||||
|
background: rgba(26, 160, 148, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-device-info span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-forecast-navigation {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #f7fafc;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-nav-btn {
|
||||||
|
background: rgba(26, 160, 148, 0.1);
|
||||||
|
border: none;
|
||||||
|
color: #1aa094;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-nav-btn:hover:not(:disabled) {
|
||||||
|
background: rgba(26, 160, 148, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-nav-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forecast-time-display {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-content {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f7fafc;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #718096;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-error {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #e53e3e;
|
||||||
|
background: rgba(229, 62, 62, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-forecast-item {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(247, 250, 252, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #1aa094;
|
||||||
|
width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-label {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #718096;
|
||||||
|
font-weight: 500;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-value {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #2d3748;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-wind-direction {
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机端适配 */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.weather-forecast-card {
|
||||||
|
right: 10px;
|
||||||
|
width: 240px;
|
||||||
|
padding: 12px;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-title {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-device-info {
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-device-info span {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-forecast-navigation {
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-nav-btn {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forecast-time-display {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-card-content {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param {
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-icon {
|
||||||
|
font-size: 8px;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-param-label,
|
||||||
|
.weather-param-value {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script src="../js/ol.js"></script>
|
<script src="../js/ol.js"></script>
|
||||||
@ -592,6 +830,32 @@
|
|||||||
<div class="map-card">
|
<div class="map-card">
|
||||||
|
|
||||||
<div id="map-container">
|
<div id="map-container">
|
||||||
|
<div id="weatherForecastCard" class="weather-forecast-card" style="display: none;">
|
||||||
|
<div class="weather-card-header">
|
||||||
|
<h3 class="weather-card-title">Windy 天气预测 :) </h3>
|
||||||
|
<button class="weather-close-btn" onclick="closeWeatherCard()">×</button>
|
||||||
|
</div>
|
||||||
|
<div id="weatherDeviceInfo" class="weather-device-info">
|
||||||
|
<span id="weatherDeviceId">设备信息</span>
|
||||||
|
<span id="weatherDeviceCoords">坐标信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-forecast-navigation">
|
||||||
|
<button id="prevForecast" class="weather-nav-btn" onclick="showPrevForecast()" disabled>
|
||||||
|
<i class="layui-icon layui-icon-left"></i>
|
||||||
|
</button>
|
||||||
|
<span id="forecastTimeDisplay" class="forecast-time-display">--:--</span>
|
||||||
|
<button id="nextForecast" class="weather-nav-btn" onclick="showNextForecast()">
|
||||||
|
<i class="layui-icon layui-icon-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="weatherForecastContent" class="weather-card-content">
|
||||||
|
<div class="weather-loading">
|
||||||
|
<i class="layui-icon layui-icon-loading layui-icon-anim-rotate"></i>
|
||||||
|
<p>正在获取天气预测数据...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="map-toolbar">
|
<div class="map-toolbar">
|
||||||
<div class="toolbar-item">
|
<div class="toolbar-item">
|
||||||
<select id="mapTypeSelectNew" class="toolbar-select" lay-filter="mapTypeNew" onchange="onMapTypeChange()">
|
<select id="mapTypeSelectNew" class="toolbar-select" lay-filter="mapTypeNew" onchange="onMapTypeChange()">
|
||||||
@ -659,11 +923,21 @@
|
|||||||
清除测距
|
清除测距
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<div class="dropdown-group">
|
||||||
|
<div class="dropdown-group-title">Windy 天气预测</div>
|
||||||
|
<div class="dropdown-item">
|
||||||
|
<label class="switch-label">
|
||||||
|
<span>启用天气预测(需网络被代理)</span>
|
||||||
|
<input type="checkbox" id="enableWeatherSwitch" onchange="toggleWeatherForecast()">
|
||||||
|
<span class="switch-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 测量状态指示器 -->
|
|
||||||
<div id="measureStatus" class="toolbar-item" style="display: none;">
|
<div id="measureStatus" class="toolbar-item" style="display: none;">
|
||||||
<span style="color: #48bb78; font-weight: bold; font-size: 12px;">测量中</span>
|
<span style="color: #48bb78; font-weight: bold; font-size: 12px;">测量中</span>
|
||||||
<button class="toolbar-btn" onclick="finishMeasuring()" style="background: #f56565; margin-left: 6px;">
|
<button class="toolbar-btn" onclick="finishMeasuring()" style="background: #f56565; margin-left: 6px;">
|
||||||
@ -673,6 +947,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -703,19 +978,25 @@
|
|||||||
// 测距
|
// 测距
|
||||||
var measureSource;
|
var measureSource;
|
||||||
var measureLayer;
|
var measureLayer;
|
||||||
|
// 天气预测
|
||||||
|
var currentWeatherDevice = null;
|
||||||
|
var weatherApiKey = 'Uxh4IdMuAvhSiBnsf4UUDVGF4e3YAp2B';
|
||||||
|
var weatherEnabled = false;
|
||||||
|
var weatherData = null;
|
||||||
|
var currentForecastIndex = 0;
|
||||||
|
|
||||||
// 天地图 API 密钥(fengyarnom@gmail.com)
|
// 天地图 API 密钥(fengyarnom@gmail.com)
|
||||||
var TIANDITU_KEY = '0c260b8a094a4e0bc507808812cefdac';
|
var TIANDITU_KEY = '0c260b8a094a4e0bc507808812cefdac';
|
||||||
|
|
||||||
var deviceList = [
|
var deviceList = [
|
||||||
[# th:each="device : ${deviceList}"]
|
/*[# th:each="device : ${deviceList}"]*/
|
||||||
{
|
{
|
||||||
deviceid: [[${device.deviceid}]],
|
deviceid: /*[[${device.deviceid}]]*/ '',
|
||||||
latitude: [[${device.latitude}]],
|
latitude: /*[[${device.latitude}]]*/ 0,
|
||||||
longitude: [[${device.longitude}]],
|
longitude: /*[[${device.longitude}]]*/ 0,
|
||||||
warning: [[${device.warning}]]
|
warning: /*[[${device.warning}]]*/ 0
|
||||||
},
|
}/*[# th:if="${!deviceStat.last}"]*/,/*[/]*/
|
||||||
[/]
|
/*[/]*/
|
||||||
];
|
];
|
||||||
|
|
||||||
var pi = 3.14159265358979324;
|
var pi = 3.14159265358979324;
|
||||||
@ -1092,11 +1373,24 @@
|
|||||||
if (feature) {
|
if (feature) {
|
||||||
var features = feature.get('features');
|
var features = feature.get('features');
|
||||||
if (features && features.length > 1) {
|
if (features && features.length > 1) {
|
||||||
|
// 集群点击,扩展视图
|
||||||
var extent = vectorSource.getExtent();
|
var extent = vectorSource.getExtent();
|
||||||
map.getView().fit(extent, {
|
map.getView().fit(extent, {
|
||||||
padding: [50, 50, 50, 50],
|
padding: [50, 50, 50, 50],
|
||||||
duration: 1000
|
duration: 1000
|
||||||
});
|
});
|
||||||
|
} else if (features && features.length === 1) {
|
||||||
|
// 单个设备点击
|
||||||
|
var deviceInfo = features[0].get('deviceInfo');
|
||||||
|
if (deviceInfo && weatherEnabled) {
|
||||||
|
showWeatherForecast(deviceInfo);
|
||||||
|
}
|
||||||
|
} else if (feature.get('deviceInfo')) {
|
||||||
|
// 直接点击设备标记
|
||||||
|
var deviceInfo = feature.get('deviceInfo');
|
||||||
|
if (weatherEnabled) {
|
||||||
|
showWeatherForecast(deviceInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1229,7 +1523,7 @@
|
|||||||
function onMapTypeChange() {
|
function onMapTypeChange() {
|
||||||
var mapType = document.getElementById('mapTypeSelectNew').value;
|
var mapType = document.getElementById('mapTypeSelectNew').value;
|
||||||
|
|
||||||
if(mapType.startsWith('google_') && [[${role}]] != "SUPER_ADMIN") {
|
if(mapType.startsWith('google_') && /*[[${role}]]*/ 'USER' != "SUPER_ADMIN") {
|
||||||
layer.msg('拒绝使用');
|
layer.msg('拒绝使用');
|
||||||
document.getElementById('mapTypeSelectNew').value = 'tianditu_terrain_hybrid';
|
document.getElementById('mapTypeSelectNew').value = 'tianditu_terrain_hybrid';
|
||||||
mapType = 'tianditu_terrain_hybrid';
|
mapType = 'tianditu_terrain_hybrid';
|
||||||
@ -1508,6 +1802,7 @@
|
|||||||
if (found && targetFeature) {
|
if (found && targetFeature) {
|
||||||
var geometry = targetFeature.getGeometry();
|
var geometry = targetFeature.getGeometry();
|
||||||
var deviceCoord = geometry.getCoordinates();
|
var deviceCoord = geometry.getCoordinates();
|
||||||
|
var deviceInfo = targetFeature.get('deviceInfo');
|
||||||
|
|
||||||
map.getView().animate({
|
map.getView().animate({
|
||||||
center: deviceCoord,
|
center: deviceCoord,
|
||||||
@ -1516,6 +1811,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
layer.msg('已定位到设备: ' + deviceId);
|
layer.msg('已定位到设备: ' + deviceId);
|
||||||
|
|
||||||
|
// 只有在天气功能开启时才显示天气预测
|
||||||
|
if (weatherEnabled) {
|
||||||
|
showWeatherForecast(deviceInfo);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
layer.msg('未找到设备: ' + deviceId);
|
layer.msg('未找到设备: ' + deviceId);
|
||||||
}
|
}
|
||||||
@ -1564,7 +1864,6 @@
|
|||||||
// 首先过滤地图上的设备显示
|
// 首先过滤地图上的设备显示
|
||||||
filterDevicesByStatus(status_type);
|
filterDevicesByStatus(status_type);
|
||||||
|
|
||||||
// 然后打开设备列表弹窗
|
|
||||||
var index = layer.open({
|
var index = layer.open({
|
||||||
title: '设备列表',
|
title: '设备列表',
|
||||||
type: 2,
|
type: 2,
|
||||||
@ -1578,7 +1877,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:根据状态过滤地图上的设备
|
|
||||||
function filterDevicesByStatus(status_type) {
|
function filterDevicesByStatus(status_type) {
|
||||||
var savedMyLocationFeature = myLocationFeature;
|
var savedMyLocationFeature = myLocationFeature;
|
||||||
vectorSource.clear();
|
vectorSource.clear();
|
||||||
@ -1587,7 +1885,6 @@
|
|||||||
vectorSource.addFeature(savedMyLocationFeature);
|
vectorSource.addFeature(savedMyLocationFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据状态类型显示对应的设备
|
|
||||||
switch(status_type) {
|
switch(status_type) {
|
||||||
case 'warning1':
|
case 'warning1':
|
||||||
for (var i = 0; i < orangeFeatures.length; i++) {
|
for (var i = 0; i < orangeFeatures.length; i++) {
|
||||||
@ -1666,14 +1963,12 @@
|
|||||||
layer.msg(showDeviceId ? '已显示设备信息' : '已隐藏设备信息');
|
layer.msg(showDeviceId ? '已显示设备信息' : '已隐藏设备信息');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 集群显示开关
|
|
||||||
function toggleCluster() {
|
function toggleCluster() {
|
||||||
showCluster = document.getElementById('showClusterSwitch').checked;
|
showCluster = document.getElementById('showClusterSwitch').checked;
|
||||||
updateLayerVisibility();
|
updateLayerVisibility();
|
||||||
layer.msg(showCluster ? '已启用集群显示' : '已禁用集群显示');
|
layer.msg(showCluster ? '已启用集群显示' : '已禁用集群显示');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新图层可见性
|
|
||||||
function updateLayerVisibility() {
|
function updateLayerVisibility() {
|
||||||
var zoom = map.getView().getZoom();
|
var zoom = map.getView().getZoom();
|
||||||
if (showCluster) {
|
if (showCluster) {
|
||||||
@ -1900,7 +2195,210 @@
|
|||||||
layer.msg('测距标记已清除');
|
layer.msg('测距标记已清除');
|
||||||
document.getElementById('mapFunctionsMenu').classList.remove('show');
|
document.getElementById('mapFunctionsMenu').classList.remove('show');
|
||||||
}
|
}
|
||||||
|
function toggleWeatherForecast() {
|
||||||
|
weatherEnabled = document.getElementById('enableWeatherSwitch').checked;
|
||||||
|
if (weatherEnabled) {
|
||||||
|
layer.msg(
|
||||||
|
'搜索设备或点击地图设备图标即可自动查询天气预测',
|
||||||
|
{time: 3000, area: ['300px', '80px']}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
layer.msg('天气预测功能已关闭');
|
||||||
|
closeWeatherCard();
|
||||||
|
}
|
||||||
|
document.getElementById('mapFunctionsMenu').classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示天气预测卡片
|
||||||
|
function showWeatherForecast(deviceInfo) {
|
||||||
|
if (!weatherEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentWeatherDevice = deviceInfo;
|
||||||
|
currentForecastIndex = 0;
|
||||||
|
weatherData = null;
|
||||||
|
|
||||||
|
document.getElementById('weatherDeviceId').textContent = '设备: ' + deviceInfo.deviceid;
|
||||||
|
document.getElementById('weatherDeviceCoords').textContent =
|
||||||
|
'坐标: ' + deviceInfo.latitude.toFixed(4) + ', ' + deviceInfo.longitude.toFixed(4);
|
||||||
|
|
||||||
|
document.getElementById('weatherForecastCard').style.display = 'block';
|
||||||
|
|
||||||
|
document.getElementById('weatherForecastContent').innerHTML =
|
||||||
|
'<div class="weather-loading">' +
|
||||||
|
'<i class="layui-icon layui-icon-loading layui-icon-anim-rotate"></i>' +
|
||||||
|
'<p>请确保网络可访问 Windy API 服务</p>' +
|
||||||
|
'<p>正在获取天气预测数据...</p>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// 重置翻页按钮
|
||||||
|
document.getElementById('prevForecast').disabled = true;
|
||||||
|
document.getElementById('nextForecast').disabled = true;
|
||||||
|
document.getElementById('forecastTimeDisplay').textContent = '--:--';
|
||||||
|
|
||||||
|
// 调用天气API
|
||||||
|
fetchWeatherData(deviceInfo.latitude, deviceInfo.longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWeatherCard() {
|
||||||
|
document.getElementById('weatherForecastCard').style.display = 'none';
|
||||||
|
currentWeatherDevice = null;
|
||||||
|
weatherData = null;
|
||||||
|
}
|
||||||
|
function showPrevForecast() {
|
||||||
|
if (!weatherData || currentForecastIndex <= 0) return;
|
||||||
|
|
||||||
|
currentForecastIndex--;
|
||||||
|
displayCurrentForecast();
|
||||||
|
updateForecastNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNextForecast() {
|
||||||
|
if (!weatherData || !weatherData.ts || currentForecastIndex >= weatherData.ts.length - 1) return;
|
||||||
|
|
||||||
|
currentForecastIndex++;
|
||||||
|
displayCurrentForecast();
|
||||||
|
updateForecastNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateForecastNavigation() {
|
||||||
|
if (!weatherData || !weatherData.ts) return;
|
||||||
|
|
||||||
|
var prevBtn = document.getElementById('prevForecast');
|
||||||
|
var nextBtn = document.getElementById('nextForecast');
|
||||||
|
var timeDisplay = document.getElementById('forecastTimeDisplay');
|
||||||
|
|
||||||
|
prevBtn.disabled = (currentForecastIndex <= 0);
|
||||||
|
nextBtn.disabled = (currentForecastIndex >= weatherData.ts.length - 1);
|
||||||
|
|
||||||
|
var timestamp = weatherData.ts[currentForecastIndex];
|
||||||
|
var date = new Date(timestamp);
|
||||||
|
timeDisplay.textContent = formatDateTime(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取天气数据
|
||||||
|
function fetchWeatherData(lat, lon) {
|
||||||
|
var requestBody = {
|
||||||
|
"lat": parseFloat(lat.toFixed(2)),
|
||||||
|
"lon": parseFloat(lon.toFixed(2)),
|
||||||
|
"model": "gfs",
|
||||||
|
"parameters": ["temp", "wind", "precip", "pressure", "rh", "windGust"],
|
||||||
|
"levels": ["surface"],
|
||||||
|
"key": weatherApiKey
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('https://api.windy.com/api/point-forecast/v2', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody)
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('网络响应状态: ' + response.status);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
weatherData = data;
|
||||||
|
currentForecastIndex = 0;
|
||||||
|
displayCurrentForecast();
|
||||||
|
updateForecastNavigation();
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('天气数据获取失败:', error);
|
||||||
|
displayWeatherError('获取天气数据失败: ' + error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayCurrentForecast() {
|
||||||
|
if (!weatherData || !weatherData.ts || weatherData.ts.length === 0) {
|
||||||
|
displayWeatherError('无可用的天气预测数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = currentForecastIndex;
|
||||||
|
var forecastHtml = '<div class="weather-forecast-item"><div class="weather-param-grid">';
|
||||||
|
|
||||||
|
if (weatherData['temp-surface'] && weatherData['temp-surface'][i] !== null) {
|
||||||
|
var temp = (weatherData['temp-surface'][i] - 273.15).toFixed(1); // 转换为摄氏度
|
||||||
|
forecastHtml += createWeatherParam('🌡️', '温度', temp + '°C');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weatherData['wind_u-surface'] && weatherData['wind_v-surface'] &&
|
||||||
|
weatherData['wind_u-surface'][i] !== null && weatherData['wind_v-surface'][i] !== null) {
|
||||||
|
var windU = weatherData['wind_u-surface'][i];
|
||||||
|
var windV = weatherData['wind_v-surface'][i];
|
||||||
|
var windSpeed = Math.sqrt(windU * windU + windV * windV).toFixed(1);
|
||||||
|
var windDir = getWindDirection(windU, windV);
|
||||||
|
forecastHtml += createWeatherParam('💨', '风速', windSpeed + ' m/s');
|
||||||
|
forecastHtml += createWeatherParam('🧭', '风向', windDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weatherData['past3hprecip-surface'] && weatherData['past3hprecip-surface'][i] !== null) {
|
||||||
|
var precip = weatherData['past3hprecip-surface'][i].toFixed(1);
|
||||||
|
forecastHtml += createWeatherParam('🌧️', '降水', precip + ' mm');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weatherData['rh-surface'] && weatherData['rh-surface'][i] !== null) {
|
||||||
|
var humidity = weatherData['rh-surface'][i].toFixed(0);
|
||||||
|
forecastHtml += createWeatherParam('💧', '湿度', humidity + '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 气压
|
||||||
|
if (weatherData['pressure-surface'] && weatherData['pressure-surface'][i] !== null) {
|
||||||
|
var pressure = (weatherData['pressure-surface'][i] / 100).toFixed(0); // 转换为百帕
|
||||||
|
forecastHtml += createWeatherParam('📊', '气压', pressure + ' hPa');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阵风
|
||||||
|
if (weatherData['gust-surface'] && weatherData['gust-surface'][i] !== null) {
|
||||||
|
var gust = weatherData['gust-surface'][i].toFixed(1);
|
||||||
|
forecastHtml += createWeatherParam('💨', '阵风', gust + ' m/s');
|
||||||
|
}
|
||||||
|
|
||||||
|
forecastHtml += '</div></div>';
|
||||||
|
document.getElementById('weatherForecastContent').innerHTML = forecastHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayWeatherError(message) {
|
||||||
|
document.getElementById('weatherForecastContent').innerHTML =
|
||||||
|
'<div class="weather-error">' +
|
||||||
|
'<i class="layui-icon layui-icon-close"></i>' +
|
||||||
|
'<p>' + message + '</p>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWeatherParam(icon, label, value) {
|
||||||
|
return '<div class="weather-param">' +
|
||||||
|
'<span class="weather-param-icon">' + icon + '</span>' +
|
||||||
|
'<span class="weather-param-label">' + label + '</span>' +
|
||||||
|
'<span class="weather-param-value">' + value + '</span>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(date) {
|
||||||
|
var month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
var day = date.getDate().toString().padStart(2, '0');
|
||||||
|
var hours = date.getHours().toString().padStart(2, '0');
|
||||||
|
var minutes = date.getMinutes().toString().padStart(2, '0');
|
||||||
|
|
||||||
|
return month + '-' + day + ' ' + hours + ':' + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWindDirection(u, v) {
|
||||||
|
var angle = Math.atan2(-u, -v) * 180 / Math.PI;
|
||||||
|
angle = (angle + 360) % 360;
|
||||||
|
|
||||||
|
var directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
|
||||||
|
var index = Math.round(angle / 45) % 8;
|
||||||
|
return directions[index];
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user