优化移动端侧边栏
··[CST 2026-04-25 Saturday 13:47:24]
This commit is contained in:
+204
@@ -0,0 +1,204 @@
|
||||
// weather.js
|
||||
|
||||
(function () {
|
||||
|
||||
const WEATHER_ID = "aside-weather";
|
||||
const LOCATION_WAIT_MS = 3000;
|
||||
const LOCATION_POLL_INTERVAL_MS = 100;
|
||||
const WEATHER_KEY = "70201debab4245379e376d116b05f8a2";
|
||||
const WEATHER_HOST = "https://ny5vxmbbkd.re.qweatherapi.com";
|
||||
const WEATHER_LANG = "zh";
|
||||
|
||||
function debugLog(label, payload) {
|
||||
console.log(`[weather.js] ${label}`, payload);
|
||||
}
|
||||
|
||||
function maskSecret(secret) {
|
||||
if (!secret || secret.length < 10) return secret;
|
||||
return `${secret.slice(0, 4)}***${secret.slice(-4)}`;
|
||||
}
|
||||
|
||||
function readExistingLocation() {
|
||||
return window.ipLocation?.result?.location || null;
|
||||
}
|
||||
|
||||
function waitForExistingLocation(timeout = LOCATION_WAIT_MS) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const existingLocation = readExistingLocation();
|
||||
if (existingLocation) {
|
||||
resolve(existingLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const timer = window.setInterval(() => {
|
||||
const location = readExistingLocation();
|
||||
if (location) {
|
||||
window.clearInterval(timer);
|
||||
resolve(location);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - startTime >= timeout) {
|
||||
window.clearInterval(timer);
|
||||
reject(new Error("等待 ipLocation 超时"));
|
||||
}
|
||||
}, LOCATION_POLL_INTERVAL_MS);
|
||||
});
|
||||
}
|
||||
|
||||
function requestLocationFallback() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!window.$?.ajax) {
|
||||
reject(new Error("jQuery 未加载,无法执行定位兜底请求"));
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: 'https://apis.map.qq.com/ws/location/v1/ip',
|
||||
data: {
|
||||
key: '你的key',
|
||||
output: 'jsonp',
|
||||
callback: '?',
|
||||
},
|
||||
dataType: 'jsonp',
|
||||
success: function (res) {
|
||||
debugLog("定位兜底返回", res);
|
||||
if (res && res.status === 0 && res.result?.location) {
|
||||
window.ipLocation = res;
|
||||
resolve(res.result.location);
|
||||
} else {
|
||||
reject(new Error("定位失败"));
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
reject(new Error("定位请求失败"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getLocation() {
|
||||
return waitForExistingLocation()
|
||||
.then(location => {
|
||||
debugLog("使用 txmap.js 定位", location);
|
||||
return location;
|
||||
})
|
||||
.catch(error => {
|
||||
debugLog("等待 txmap.js 定位失败,转兜底请求", error);
|
||||
return requestLocationFallback();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getWeather(lng, lat) {
|
||||
const location = `${lng.toFixed(2)},${lat.toFixed(2)}`;
|
||||
const params = new URLSearchParams({
|
||||
location,
|
||||
lang: WEATHER_LANG,
|
||||
key: WEATHER_KEY
|
||||
});
|
||||
const requestUrl = `${WEATHER_HOST}/v7/weather/now?${params.toString()}`;
|
||||
|
||||
debugLog("天气请求参数", {
|
||||
host: WEATHER_HOST,
|
||||
location,
|
||||
lang: WEATHER_LANG,
|
||||
key: maskSecret(WEATHER_KEY)
|
||||
});
|
||||
|
||||
return fetch(requestUrl, {
|
||||
headers: {
|
||||
Accept: "application/json"
|
||||
}
|
||||
}).then(async res => {
|
||||
const responseText = await res.text();
|
||||
let data;
|
||||
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (parseError) {
|
||||
debugLog("天气接口返回非 JSON", {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
body: responseText
|
||||
});
|
||||
throw new Error(`天气接口返回非 JSON,HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
debugLog("天气接口响应", {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
body: data
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const detail = data?.error?.detail || data?.msg || `HTTP ${res.status}`;
|
||||
throw new Error(`天气接口请求失败:${detail}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function renderWeather(data) {
|
||||
const el = document.getElementById(WEATHER_ID);
|
||||
if (!el) return;
|
||||
|
||||
if (data.code !== "200") {
|
||||
debugLog("天气接口业务错误", data);
|
||||
el.innerHTML = `天气获取失败:${data.code || "未知错误"}`;
|
||||
return;
|
||||
}
|
||||
|
||||
const now = data.now;
|
||||
|
||||
const emojiMap = {
|
||||
"晴": "☀️",
|
||||
"多云": "⛅",
|
||||
"阴": "☁️",
|
||||
"雨": "🌧️",
|
||||
"雪": "❄️",
|
||||
"雷": "⛈️"
|
||||
};
|
||||
|
||||
const emoji = emojiMap[now.text] || "🌡️";
|
||||
const weatherSentence = `当前天气${now.text},气温${now.temp}℃,体感${now.feelsLike}℃,${now.windDir}${now.windScale}级,湿度${now.humidity}%,能见度${now.vis}km。`;
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="aside-weather-card">
|
||||
<div class="weather-top">
|
||||
</div>
|
||||
<div class="weather-info">
|
||||
${weatherSentence}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
function initWeather() {
|
||||
const el = document.getElementById(WEATHER_ID);
|
||||
if (!el) return;
|
||||
|
||||
el.innerHTML = "🌥️ 获取天气中...";
|
||||
|
||||
getLocation()
|
||||
.then(loc => getWeather(loc.lng, loc.lat))
|
||||
.then(renderWeather)
|
||||
.catch(err => {
|
||||
console.error("[weather.js] 天气加载失败", err);
|
||||
el.innerHTML = `🌧️ 天气加载失败:${err.message || "未知错误"}`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 🚀 初次加载
|
||||
window.addEventListener("load", initWeather);
|
||||
|
||||
// 🔄 pjax
|
||||
document.addEventListener("pjax:complete", initWeather);
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user