This commit is contained in:
2025-09-07 22:00:47 +08:00
parent 0a8b20e450
commit c2b2e84416
63 changed files with 1939 additions and 1343 deletions

View File

@@ -0,0 +1,145 @@
/* 背景样式文件 */
body {
background: linear-gradient(135deg, #e8f5e8 0%, #d4f1d4 25%, #c8ecc8 50%, #b8e6b8 75%, #a8d5ba 100%);
background-attachment: fixed;
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
position: relative;
overflow-x: hidden;
}
/* 背景渐变动画 */
@keyframes gradientShift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* 背景装饰元素 */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(circle at 20% 80%, rgba(168, 213, 186, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(107, 183, 123, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(200, 236, 200, 0.1) 0%, transparent 50%);
pointer-events: none;
z-index: -1;
}
/* 浮动装饰圆点 */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(168, 213, 186, 0.3), transparent),
radial-gradient(2px 2px at 40px 70px, rgba(107, 183, 123, 0.2), transparent),
radial-gradient(1px 1px at 90px 40px, rgba(200, 236, 200, 0.4), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(168, 213, 186, 0.2), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(107, 183, 123, 0.3), transparent);
background-repeat: repeat;
background-size: 200px 100px;
animation: float 20s linear infinite;
pointer-events: none;
z-index: -1;
}
@keyframes float {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
100% {
transform: translateY(0px);
}
}
/* 云朵装饰效果 */
.container::before {
content: '';
position: absolute;
top: -50px;
right: -50px;
width: 200px;
height: 100px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50px;
box-shadow:
-30px 20px 0 rgba(255, 255, 255, 0.08),
30px 40px 0 rgba(255, 255, 255, 0.06);
animation: cloudFloat 25s ease-in-out infinite;
pointer-events: none;
z-index: -1;
}
.container::after {
content: '';
position: absolute;
bottom: -30px;
left: -30px;
width: 150px;
height: 80px;
background: rgba(255, 255, 255, 0.08);
border-radius: 40px;
box-shadow:
20px 15px 0 rgba(255, 255, 255, 0.06),
-20px 25px 0 rgba(255, 255, 255, 0.04);
animation: cloudFloat 30s ease-in-out infinite reverse;
pointer-events: none;
z-index: -1;
}
@keyframes cloudFloat {
0%, 100% {
transform: translateX(0px) translateY(0px);
}
25% {
transform: translateX(20px) translateY(-10px);
}
50% {
transform: translateX(-10px) translateY(-20px);
}
75% {
transform: translateX(15px) translateY(-5px);
}
}
/* 响应式背景调整 */
@media (max-width: 768px) {
body::after {
background-size: 150px 75px;
}
.container::before,
.container::after {
display: none;
}
}
@media (max-width: 480px) {
body {
background: linear-gradient(135deg, #e8f5e8 0%, #d4f1d4 50%, #a8d5ba 100%);
animation: none;
}
body::before,
body::after {
display: none;
}
}

View File

@@ -0,0 +1,412 @@
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
line-height: 1.6;
color: #333;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* 头部样式 */
.header {
text-align: center;
margin-bottom: 30px;
}
.header h1 {
color: #2d5a3d;
font-size: 2.5rem;
font-weight: 300;
margin-bottom: 10px;
}
/* 搜索区域 */
.search-section {
margin-bottom: 30px;
}
.search-box {
display: flex;
justify-content: center;
gap: 10px;
max-width: 500px;
margin: 0 auto;
}
#cityInput {
flex: 1;
padding: 12px 16px;
border: 2px solid #a8d5ba;
border-radius: 25px;
font-size: 16px;
outline: none;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.9);
}
#cityInput:focus {
border-color: #6bb77b;
box-shadow: 0 0 10px rgba(107, 183, 123, 0.3);
}
#searchBtn {
padding: 12px 24px;
background: linear-gradient(135deg, #6bb77b, #5a9f6a);
color: white;
border: none;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
}
#searchBtn:hover {
background: linear-gradient(135deg, #5a9f6a, #4a8759);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(107, 183, 123, 0.4);
}
/* 加载动画 */
.loading {
text-align: center;
padding: 40px;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e8f5e8;
border-top: 4px solid #6bb77b;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 天气容器 */
.weather-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(168, 213, 186, 0.3);
}
/* 位置信息 */
.location-info {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #e8f5e8;
}
.location-info h2 {
color: #2d5a3d;
font-size: 2rem;
margin-bottom: 5px;
}
.location-info p {
color: #666;
font-size: 14px;
}
/* 当前天气 */
.current-weather {
margin-bottom: 30px;
}
.weather-main {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.temperature {
font-size: 4rem;
font-weight: 300;
color: #2d5a3d;
}
.unit {
font-size: 2rem;
color: #6bb77b;
}
.weather-desc p:first-child {
font-size: 1.5rem;
color: #2d5a3d;
margin-bottom: 5px;
}
.weather-desc p:last-child {
color: #666;
font-size: 14px;
}
/* 更新时间 */
.update-time {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e8f5e8;
color: #666;
font-size: 14px;
}
/* 错误信息 */
.error-message {
text-align: center;
padding: 40px;
background: rgba(255, 107, 107, 0.1);
border-radius: 15px;
border: 1px solid rgba(255, 107, 107, 0.2);
color: #d63031;
}
/* 平板端适配 (768px - 1024px) */
@media (min-width: 768px) and (max-width: 1024px) {
.container {
padding: 25px;
}
.header h1 {
font-size: 2.2rem;
}
.temperature {
font-size: 3.5rem;
}
.index-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 电脑端适配 (1024px+) */
@media (min-width: 1024px) {
.container {
padding: 40px;
}
.weather-container {
padding: 40px;
}
.weather-main {
justify-content: space-around;
}
.index-grid {
grid-template-columns: repeat(3, 1fr);
}
.search-box {
max-width: 600px;
}
}
/* 手机端适配 (768px以下) */
@media (max-width: 767px) {
.container {
padding: 15px;
}
.header h1 {
font-size: 2rem;
}
.search-box {
flex-direction: column;
gap: 15px;
}
#searchBtn {
padding: 14px 24px;
}
.weather-container {
padding: 20px;
margin: 0 -5px;
}
.weather-main {
flex-direction: column;
text-align: center;
gap: 20px;
}
.temperature {
font-size: 3rem;
}
.index-grid {
grid-template-columns: 1fr;
gap: 15px;
}
.index-item {
padding: 15px;
}
.index-icon {
font-size: 1.5rem;
width: 40px;
margin-right: 12px;
}
}
/* 超小屏幕适配 (480px以下) */
@media (max-width: 480px) {
.container {
padding: 10px;
}
.header h1 {
font-size: 1.8rem;
}
.weather-container {
padding: 15px;
border-radius: 15px;
}
.temperature {
font-size: 2.5rem;
}
}
/* 预报区域样式 */
.forecast-section {
margin-top: 30px;
padding: 20px;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
backdrop-filter: blur(10px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.forecast-section h3 {
color: #2d5a3d;
font-size: 1.5rem;
margin-bottom: 20px;
text-align: center;
}
.forecast-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.forecast-item {
background: rgba(255, 255, 255, 0.8);
border-radius: 15px;
padding: 15px;
text-align: center;
border: 1px solid rgba(168, 213, 186, 0.3);
transition: all 0.3s ease;
}
.forecast-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.forecast-date {
font-weight: bold;
color: #2d5a3d;
margin-bottom: 10px;
font-size: 1.1rem;
}
.forecast-weather {
margin-bottom: 10px;
}
.weather-day {
color: #666;
font-size: 0.9rem;
}
.weather-night {
color: #888;
font-size: 0.85rem;
}
.forecast-temp {
margin-bottom: 10px;
display: flex;
justify-content: center;
gap: 10px;
}
.temp-high {
color: #ff6b6b;
font-weight: bold;
font-size: 1.2rem;
}
.temp-low {
color: #4ecdc4;
font-size: 1rem;
}
.forecast-wind {
color: #666;
font-size: 0.85rem;
margin-bottom: 5px;
}
.forecast-humidity {
color: #888;
font-size: 0.8rem;
}
/* 预报区域响应式设计 */
@media (max-width: 768px) {
.forecast-grid {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}
.forecast-item {
padding: 12px;
}
.forecast-date {
font-size: 1rem;
}
.temp-high {
font-size: 1.1rem;
}
}
@media (max-width: 480px) {
.forecast-section {
padding: 15px;
}
.forecast-grid {
grid-template-columns: repeat(2, 1fr);
}
}

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气预报</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/background.css">
</head>
<body>
<div class="container">
<header class="header">
<h1>天气预报</h1>
</header>
<div class="search-section">
<div class="search-box">
<input type="text" id="cityInput" placeholder="请输入城市名称(如:北京)" value="北京">
<button id="searchBtn">查询天气</button>
</div>
</div>
<div class="loading" id="loading" style="display: none;">
<div class="spinner"></div>
<p>正在获取天气信息...</p>
</div>
<div class="weather-container" id="weatherContainer" style="display: none;">
<div class="location-info">
<h2 id="locationName"></h2>
<p id="locationDetail"></p>
</div>
<div class="current-weather">
<div class="weather-main">
<div class="temperature">
<span id="temperature"></span>
<span class="unit">°C</span>
</div>
<div class="weather-desc">
<p id="weatherCondition"></p>
<p id="feelsLike"></p>
</div>
</div>
</div>
<div class="forecast-section">
<h3>未来天气预报</h3>
<div class="forecast-grid" id="forecastGrid">
<!-- 预报数据将通过JavaScript动态生成 -->
</div>
</div>
<div class="update-time">
<p>更新时间:<span id="updateTime"></span></p>
</div>
</div>
<div class="error-message" id="errorMessage" style="display: none;">
<p>获取天气信息失败,请稍后重试</p>
</div>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,211 @@
// 天气查询应用
class WeatherApp {
constructor() {
this.apiEndpoints = [
"https://60s.api.shumengya.top/v2/weather/forecast"
];
this.currentEndpointIndex = 0;
this.init();
}
init() {
this.bindEvents();
// 页面加载时自动查询北京天气
this.searchWeather('北京');
}
bindEvents() {
const searchBtn = document.getElementById('searchBtn');
const cityInput = document.getElementById('cityInput');
searchBtn.addEventListener('click', () => {
const city = cityInput.value.trim();
if (city) {
this.searchWeather(city);
} else {
this.showError('请输入城市名称');
}
});
cityInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const city = cityInput.value.trim();
if (city) {
this.searchWeather(city);
} else {
this.showError('请输入城市名称');
}
}
});
// 防止输入框为空时查询
cityInput.addEventListener('input', () => {
const searchBtn = document.getElementById('searchBtn');
searchBtn.disabled = !cityInput.value.trim();
});
}
async searchWeather(city) {
this.showLoading();
for (let i = 0; i < this.apiEndpoints.length; i++) {
try {
const endpoint = this.apiEndpoints[this.currentEndpointIndex];
const response = await this.fetchWeatherData(endpoint, city);
if (response && response.code === 200) {
this.displayWeatherData(response.data);
return;
}
} catch (error) {
console.warn(`API ${this.apiEndpoints[this.currentEndpointIndex]} 请求失败:`, error);
}
// 切换到下一个API端点
this.currentEndpointIndex = (this.currentEndpointIndex + 1) % this.apiEndpoints.length;
}
// 所有API都失败了
this.showError('获取天气信息失败,请检查网络连接或稍后重试');
}
async fetchWeatherData(endpoint, city) {
const url = `${endpoint}?query=${encodeURIComponent(city)}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
timeout: 10000
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
displayWeatherData(data) {
const { location, forecast } = data;
// 显示位置信息
document.getElementById('locationName').textContent = location.formatted;
document.getElementById('locationDetail').textContent =
`${location.province} ${location.city} | 邮编: ${location.zip_code}`;
// 使用第一天的预报数据作为当前天气(今天的天气)
const todayWeather = forecast[0];
// 显示当前天气(使用今天的最高温度)
document.getElementById('temperature').textContent = todayWeather.temperature_high;
document.getElementById('weatherCondition').textContent =
`${todayWeather.weather_day}${todayWeather.weather_night}`;
// 体感温度(使用温度范围)
document.getElementById('feelsLike').textContent =
`温度范围 ${todayWeather.temperature_low}°C - ${todayWeather.temperature_high}°C`;
// 显示更新时间(使用当前时间)
document.getElementById('updateTime').textContent =
`${this.formatDate(new Date())} (基于预报数据)`;
// 显示天气预报
this.displayForecast(forecast);
this.showWeatherContainer();
}
displayForecast(forecast) {
const forecastGrid = document.getElementById('forecastGrid');
forecastGrid.innerHTML = '';
forecast.forEach((day, index) => {
const forecastItem = document.createElement('div');
forecastItem.className = 'forecast-item';
forecastItem.innerHTML = `
<div class="forecast-date">${day.date_desc}</div>
<div class="forecast-weather">
<div class="weather-day">${day.weather_day}</div>
<div class="weather-night">${day.weather_night}</div>
</div>
<div class="forecast-temp">
<span class="temp-high">${day.temperature_high}°</span>
<span class="temp-low">${day.temperature_low}°</span>
</div>
<div class="forecast-wind">
<div>${day.wind_direction_day} ${day.wind_strength_day}</div>
</div>
<div class="forecast-humidity">湿度: ${day.humidity}%</div>
`;
forecastGrid.appendChild(forecastItem);
});
}
// 华氏度转摄氏度
fahrenheitToCelsius(fahrenheit) {
const celsius = (fahrenheit - 32) * 5 / 9;
return Math.round(celsius * 10) / 10; // 保留一位小数
}
// 格式化时间
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
showLoading() {
document.getElementById('loading').style.display = 'block';
document.getElementById('weatherContainer').style.display = 'none';
document.getElementById('errorMessage').style.display = 'none';
}
showWeatherContainer() {
document.getElementById('loading').style.display = 'none';
document.getElementById('weatherContainer').style.display = 'block';
document.getElementById('errorMessage').style.display = 'none';
}
showError(message) {
document.getElementById('loading').style.display = 'none';
document.getElementById('weatherContainer').style.display = 'none';
const errorElement = document.getElementById('errorMessage');
errorElement.style.display = 'block';
errorElement.querySelector('p').textContent = message;
}
}
// 页面加载完成后初始化应用
document.addEventListener('DOMContentLoaded', () => {
new WeatherApp();
});
// 添加页面可见性检测,当页面重新可见时刷新数据
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
const cityInput = document.getElementById('cityInput');
const city = cityInput.value.trim() || '北京';
// 延迟1秒刷新避免频繁请求
setTimeout(() => {
if (window.weatherApp) {
window.weatherApp.searchWeather(city);
}
}, 1000);
}
});
// 将应用实例暴露到全局,方便调试和其他功能调用
window.weatherApp = null;
document.addEventListener('DOMContentLoaded', () => {
window.weatherApp = new WeatherApp();
});

View File

@@ -0,0 +1,3 @@
[
"https://60s.api.shumengya.top/v2/weather/forecast"
]

View File

@@ -0,0 +1,101 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"location": {
"province": "北京",
"city": "北京",
"town": "北京",
"formatted": "北京",
"location_id": "101010100",
"detail_url": "http://www.weather.com.cn/weather/101010100.shtml",
"is_province": true,
"is_city": false,
"is_town": false,
"area_code": "10",
"zip_code": "100000"
},
"forecast": [
{
"date": "9/4",
"date_desc": "今天",
"weather_day": "多云",
"weather_night": "阴",
"weather_code_day": "01",
"weather_code_night": "02",
"temperature_high": 31,
"temperature_low": 21,
"wind_direction_day": "南风",
"wind_direction_night": "南风",
"wind_strength_day": "\u003C3级",
"wind_strength_night": "\u003C3级",
"rainfall": 96.1,
"humidity": 83
},
{
"date": "9/5",
"date_desc": "星期五",
"weather_day": "中雨",
"weather_night": "多云",
"weather_code_day": "08",
"weather_code_night": "01",
"temperature_high": 23,
"temperature_low": 19,
"wind_direction_day": "西南风",
"wind_direction_night": "北风",
"wind_strength_day": "\u003C3级",
"wind_strength_night": "\u003C3级",
"rainfall": 100,
"humidity": 68
},
{
"date": "9/6",
"date_desc": "星期六",
"weather_day": "多云",
"weather_night": "晴",
"weather_code_day": "01",
"weather_code_night": "00",
"temperature_high": 30,
"temperature_low": 19,
"wind_direction_day": "南风",
"wind_direction_night": "西南风",
"wind_strength_day": "\u003C3级",
"wind_strength_night": "\u003C3级",
"rainfall": 85.2,
"humidity": 36
},
{
"date": "9/7",
"date_desc": "星期日",
"weather_day": "多云",
"weather_night": "晴",
"weather_code_day": "01",
"weather_code_night": "00",
"temperature_high": 29,
"temperature_low": 20,
"wind_direction_day": "北风",
"wind_direction_night": "北风",
"wind_strength_day": "\u003C3级",
"wind_strength_night": "\u003C3级",
"rainfall": 87.3,
"humidity": 27
},
{
"date": "9/8",
"date_desc": "星期一",
"weather_day": "多云",
"weather_night": "多云",
"weather_code_day": "01",
"weather_code_night": "01",
"temperature_high": 28,
"temperature_low": 20,
"wind_direction_day": "南风",
"wind_direction_night": "南风",
"wind_strength_day": "\u003C3级",
"wind_strength_night": "\u003C3级",
"rainfall": 84.8,
"humidity": 41
}
]
}
}