// 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 = `
${weatherSentence}
`; } 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); })();