把大致框架搭好1

This commit is contained in:
2025-09-02 08:40:37 +08:00
parent 56659d9790
commit b139fb14d9
108 changed files with 25897 additions and 3 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,409 @@
/* 基础样式重置 */
* {
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;
}
/* 天气详情 */
.weather-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
}
.detail-item {
background: rgba(168, 213, 186, 0.1);
padding: 15px;
border-radius: 12px;
text-align: center;
border: 1px solid rgba(168, 213, 186, 0.2);
}
.detail-item .label {
display: block;
color: #666;
font-size: 12px;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.detail-item span:last-child {
color: #2d5a3d;
font-weight: 600;
font-size: 16px;
}
/* 生活指数 */
.life-index {
margin-top: 30px;
}
.life-index h3 {
color: #2d5a3d;
font-size: 1.5rem;
margin-bottom: 20px;
text-align: center;
}
.index-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}
.index-item {
display: flex;
align-items: center;
background: rgba(168, 213, 186, 0.05);
padding: 20px;
border-radius: 15px;
border: 1px solid rgba(168, 213, 186, 0.2);
transition: all 0.3s ease;
}
.index-item:hover {
background: rgba(168, 213, 186, 0.1);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(168, 213, 186, 0.2);
}
.index-icon {
font-size: 2rem;
margin-right: 15px;
width: 50px;
text-align: center;
}
.index-content h4 {
color: #2d5a3d;
font-size: 16px;
margin-bottom: 5px;
}
.index-content p {
color: #6bb77b;
font-weight: 600;
margin-bottom: 3px;
}
.index-content span {
color: #666;
font-size: 13px;
line-height: 1.4;
}
/* 更新时间 */
.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;
}
.weather-details {
grid-template-columns: repeat(3, 1fr);
}
.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;
}
.weather-details {
grid-template-columns: repeat(6, 1fr);
}
.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;
}
.weather-details {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.detail-item {
padding: 12px;
}
.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;
}
.weather-details {
grid-template-columns: 1fr;
}
.detail-item {
padding: 10px;
}
}

View File

@@ -0,0 +1,140 @@
<!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 class="weather-details">
<div class="detail-item">
<span class="label">湿度</span>
<span id="humidity"></span>
</div>
<div class="detail-item">
<span class="label">风向</span>
<span id="windDirection"></span>
</div>
<div class="detail-item">
<span class="label">风力</span>
<span id="windStrength"></span>
</div>
<div class="detail-item">
<span class="label">气压</span>
<span id="pressure"></span>
</div>
<div class="detail-item">
<span class="label">能见度</span>
<span id="visibility"></span>
</div>
<div class="detail-item">
<span class="label">空气质量</span>
<span id="aqi"></span>
</div>
</div>
</div>
<div class="life-index">
<h3>生活指数</h3>
<div class="index-grid">
<div class="index-item">
<div class="index-icon comfort">🌡️</div>
<div class="index-content">
<h4>舒适度</h4>
<p id="comfortLevel"></p>
<span id="comfortDesc"></span>
</div>
</div>
<div class="index-item">
<div class="index-icon clothing">👕</div>
<div class="index-content">
<h4>穿衣指数</h4>
<p id="clothingLevel"></p>
<span id="clothingDesc"></span>
</div>
</div>
<div class="index-item">
<div class="index-icon umbrella">☂️</div>
<div class="index-content">
<h4>雨伞指数</h4>
<p id="umbrellaLevel"></p>
<span id="umbrellaDesc"></span>
</div>
</div>
<div class="index-item">
<div class="index-icon uv">☀️</div>
<div class="index-content">
<h4>紫外线</h4>
<p id="uvLevel"></p>
<span id="uvDesc"></span>
</div>
</div>
<div class="index-item">
<div class="index-icon travel">🚗</div>
<div class="index-content">
<h4>出行指数</h4>
<p id="travelLevel"></p>
<span id="travelDesc"></span>
</div>
</div>
<div class="index-item">
<div class="index-icon sport">🏃</div>
<div class="index-content">
<h4>运动指数</h4>
<p id="sportLevel"></p>
<span id="sportDesc"></span>
</div>
</div>
</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,252 @@
// 天气查询应用
class WeatherApp {
constructor() {
this.apiEndpoints = [
'https://60s-cf.viki.moe',
'https://60s.viki.moe',
'https://60s.b23.run',
'https://60s.114128.xyz',
'https://60s-cf.114128.xyz'
];
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}/v2/weather?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, realtime } = data;
// 显示位置信息
document.getElementById('locationName').textContent = location.formatted;
document.getElementById('locationDetail').textContent =
`${location.province} ${location.city} | 邮编: ${location.zip_code}`;
// 显示当前天气
document.getElementById('temperature').textContent = realtime.temperature;
document.getElementById('weatherCondition').textContent = realtime.weather;
// 体感温度转换API返回的是华氏度需要转换为摄氏度
const feelsLikeCelsius = this.fahrenheitToCelsius(realtime.temperature_feels_like);
document.getElementById('feelsLike').textContent =
`体感温度 ${feelsLikeCelsius}°C`;
// 显示天气详情
document.getElementById('humidity').textContent = `${realtime.humidity}%`;
document.getElementById('windDirection').textContent = realtime.wind_direction;
document.getElementById('windStrength').textContent = realtime.wind_strength;
document.getElementById('pressure').textContent = `${realtime.pressure} hPa`;
document.getElementById('visibility').textContent = realtime.visibility;
// 空气质量显示
const aqiElement = document.getElementById('aqi');
aqiElement.textContent = `${realtime.aqi} (PM2.5: ${realtime.pm25})`;
aqiElement.className = this.getAQIClass(realtime.aqi);
// 显示生活指数
const lifeIndex = realtime.life_index;
this.displayLifeIndex('comfort', lifeIndex.comfort);
this.displayLifeIndex('clothing', lifeIndex.clothing);
this.displayLifeIndex('umbrella', lifeIndex.umbrella);
this.displayLifeIndex('uv', lifeIndex.uv);
this.displayLifeIndex('travel', lifeIndex.travel);
this.displayLifeIndex('sport', lifeIndex.sport);
// 显示更新时间
document.getElementById('updateTime').textContent =
`${realtime.updated} (${realtime.updated_at})`;
this.showWeatherContainer();
}
displayLifeIndex(type, indexData) {
const levelElement = document.getElementById(`${type}Level`);
const descElement = document.getElementById(`${type}Desc`);
if (levelElement && descElement && indexData) {
levelElement.textContent = indexData.level;
descElement.textContent = indexData.desc;
// 根据指数级别设置颜色
levelElement.className = this.getIndexLevelClass(indexData.level);
}
}
getAQIClass(aqi) {
if (aqi <= 50) return 'aqi-good';
if (aqi <= 100) return 'aqi-moderate';
if (aqi <= 150) return 'aqi-unhealthy-sensitive';
if (aqi <= 200) return 'aqi-unhealthy';
if (aqi <= 300) return 'aqi-very-unhealthy';
return 'aqi-hazardous';
}
getIndexLevelClass(level) {
const levelMap = {
'优': 'level-excellent',
'良': 'level-good',
'适宜': 'level-suitable',
'舒适': 'level-comfortable',
'较适宜': 'level-fairly-suitable',
'不宜': 'level-unsuitable',
'较不宜': 'level-fairly-unsuitable',
'带伞': 'level-bring-umbrella',
'最弱': 'level-weakest',
'弱': 'level-weak',
'中等': 'level-moderate',
'强': 'level-strong',
'很强': 'level-very-strong'
};
return levelMap[level] || 'level-default';
}
// 华氏度转摄氏度
fahrenheitToCelsius(fahrenheit) {
const celsius = (fahrenheit - 32) * 5 / 9;
return Math.round(celsius * 10) / 10; // 保留一位小数
}
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;
}
}
// 添加生活指数级别样式
const style = document.createElement('style');
style.textContent = `
.aqi-good { color: #52c41a; }
.aqi-moderate { color: #faad14; }
.aqi-unhealthy-sensitive { color: #fa8c16; }
.aqi-unhealthy { color: #f5222d; }
.aqi-very-unhealthy { color: #a0206e; }
.aqi-hazardous { color: #722ed1; }
.level-excellent, .level-suitable, .level-comfortable { color: #52c41a; }
.level-good, .level-fairly-suitable { color: #1890ff; }
.level-bring-umbrella, .level-moderate { color: #faad14; }
.level-unsuitable, .level-fairly-unsuitable { color: #f5222d; }
.level-weakest, .level-weak { color: #52c41a; }
.level-strong, .level-very-strong { color: #fa8c16; }
.level-default { color: #666; }
`;
document.head.appendChild(style);
// 页面加载完成后初始化应用
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,7 @@
[
"https://60s-cf.viki.moe",
"https://60s.viki.moe",
"https://60s.b23.run",
"https://60s.114128.xyz",
"https://60s-cf.114128.xyz"
]

View File

@@ -0,0 +1,68 @@
{
"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"
},
"realtime": {
"weather": "小雨转多云",
"weather_desc": "未知",
"weather_code": "d7",
"temperature": 26,
"temperature_feels_like": 81.1,
"humidity": 78,
"wind_direction": "南风转北风",
"wind_strength": "\u003C3级",
"wind_speed": "5km/h",
"pressure": 1008,
"visibility": "8km",
"aqi": 37,
"pm25": 37,
"rainfall": 0,
"rainfall_24h": 0,
"updated": "2025-08-29 08:00:00",
"updated_at": "15:10",
"life_index": {
"comfort": {
"level": "舒适",
"desc": "白天温度宜人,风力不大。"
},
"clothing": {
"level": "舒适",
"desc": "建议穿长袖衬衫单裤等服装。"
},
"umbrella": {
"level": "带伞",
"desc": "有降水,带雨伞,短期外出可收起雨伞。"
},
"uv": {
"level": "最弱",
"desc": "辐射弱涂擦SPF8-12防晒护肤品。"
},
"car_wash": {
"level": "不宜",
"desc": "有雨,雨水和泥水会弄脏爱车。"
},
"travel": {
"level": "适宜",
"desc": "较弱降水和微风将伴您共赴旅程。"
},
"sport": {
"level": "较不宜",
"desc": "有降水,推荐您在室内进行休闲运动。"
}
}
}
}
}