// ==UserScript== // @name xxx // @namespace https://docs.scriptcat.org/ // @version 1.5 // @description 底部工具栏:vh单位防溢出+自定义SVG天气图标+环形电量修复 // @author 脚本助手 // @match *://www.jkcxsn.cn/* // @grant none // @connect devapi.qweather.com // @connect geoapi.qweather.com // ==/UserScript== (function() { 'use strict'; if (window.top !== window || document.getElementById('custom-bottom-toolbar')) return; let lastIndex = -1; const textList = [ "信息与无限 , Relink重塑未来", "天空属于哈夫克", "世界向左 , 哈夫克向右", "资源无限供应 , 援军已在路上", "科技重塑秩序 , 公平属于计算" ]; const WEATHER_KEY = "35459b2323b940b6b823a00efcac9330"; function getRandomText() { let idx; do { idx = Math.floor(Math.random() * textList.length); } while (idx === lastIndex); lastIndex = idx; return textList[idx]; } // 农历 function getLunarDate(y, m, d) { const lunarInfo = [ 0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2, 0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977, 0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970 ]; const lunarMon = ["正月","二月","三月","四月","五月","六月","七月","八月","九月","十月","冬月","腊月"]; const lunarDay = ["初一","初二","初三","初四","初五","初六","初七","初八","初九","初十", "十一","十二","十三","十四","十五","十六","十七","十八","十九","二十", "廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十"]; function getLunarDays(y, m) { return (lunarInfo[y-1900] & (0x10000>>m)) ? 30 : 29; } let baseDate = new Date(1900,0,31); let targetDate = new Date(y,m-1,d); let offset = Math.floor((targetDate - baseDate) / 86400000); let ly = 1900, lm = 1, ld = 1; while(offset>0){ let days = getLunarDays(ly, lm); if(offset12){ lm=1; ly++; } } ld += offset; return { month:lunarMon[lm-1], day:lunarDay[ld-1] }; } function getFullDateStr() { const now = new Date(); const y = now.getFullYear(), m = now.getMonth()+1, d = now.getDate(); const weekArr = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]; const week = weekArr[now.getDay()]; const lunar = getLunarDate(y,m,d); return `${y}年${m}月${d}日 ${week} 农历${lunar.month}${lunar.day}`; } // 样式:左右分栏 + vh自适应 const style = document.createElement('style'); style.textContent = ` #custom-bottom-toolbar { position: fixed; bottom: 3vh; left: 15vw; width: 70vw; height: 14vh; background: #fff; border: 1px solid #ddd; border-radius: 12px; box-shadow: 0 -2px 14px rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: space-between; padding: 0 2vw; box-sizing: border-box; z-index: 9999999; font-family: "Microsoft YaHei", sans-serif; user-select: none; } .toolbar-left { display: flex; align-items: center; gap: 1vw; height: 100%; width: 32%; flex-shrink: 0; } .toolbar-icon { height: 7vh; width: auto; border-radius: 6px; object-fit: contain; flex-shrink: 0; } .left-text-group { display: flex; flex-direction: column; justify-content: center; gap: 0.5vh; } .left-title { font-size: 1.7vh; font-weight: bold; color: #000; } .toolbar-text { font-size: 1.4vh; color: #333; cursor: pointer; } .toolbar-text:hover { color: #0066cc; } .toolbar-center { width: 45%; display: flex; justify-content: center; align-items: center; height: 100%; flex-shrink: 0; gap: 1vw; } .center-card { background: #f7f8fa; border-radius: 0.8vh; padding: 1vh; box-shadow: 0 1px 4px rgba(0,0,0,0.08); box-sizing: border-box; overflow: hidden; } .weather-card { width: 24vh; height: 12vh; display: flex; flex-direction: row; align-items: center; gap: 1vh; } .battery-card { width: 10vh; height: 12vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1vh; } /* 天气左侧区域 */ .weather-left { width: 7vh; display: flex; flex-direction: column; justify-content: center; gap: 0.6vh; } .weather-city { font-size: 1.6vh; font-weight: bold; color: #222; white-space: nowrap; } .weather-desc-temp { font-size: 1.3vh; color: #555; white-space: nowrap; } /* 天气右侧网格 */ .weather-right { flex: 1; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 0.5vh; height: 100%; } .weather-mini { background: #fff; border-radius: 0.5vh; padding: 0.5vh; display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: 1.1vh; } .mini-top { display: flex; align-items: center; gap: 0.3vh; color: #666; margin-bottom: 0.2vh; white-space: nowrap; } .mini-top svg { width: 1.2vh; height: 1.2vh; fill: #666; } .mini-value { color: #222; font-weight: 500; } /* 电池 */ .battery-wrap { position: relative; width: 5vh; height: 5vh; border-radius: 50%; } .battery-inner { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 4vh; height: 4vh; background: #f7f8fa; border-radius: 50%; } .battery-info { display: flex; align-items: center; gap: 0.3vh; font-size: 1.2vh; font-weight: bold; } .bat-dot { width: 0.7vh; height: 0.7vh; border-radius: 50%; } /* 右侧 */ .toolbar-right { width: 23%; display: flex; flex-direction: column; align-items: flex-end; justify-content: center; height: 100%; gap: 0.3vh; flex-shrink: 0; } #toolbar-time { font-size: 1.8vh; font-weight: bold; color: #222; } #toolbar-date { font-size: 1.2vh; color: #666; text-align: right; line-height: 1.3; } .toolbar-relink { font-size: 1.1vh; color: #999; margin-top: 0.4vh; } `; document.head.appendChild(style); // SVG 图标 const iconWind = ``; const iconHum = ``; const iconFeel = ``; const iconRain = ``; const toolbar = document.createElement('div'); toolbar.id = 'custom-bottom-toolbar'; document.body.appendChild(toolbar); function renderToolbar() { toolbar.innerHTML = `
哈夫克
${getRandomText()}
加载中
-- / --℃
${iconWind}风向/风速
--
${iconHum}湿度
--
${iconFeel}体感温度
--
${iconRain}1小时降水
--
--%
00:00:00
${getFullDateStr()}
`; document.getElementById('randText').onclick = () => { document.getElementById('randText').textContent = getRandomText(); }; } renderToolbar(); // 时间 setInterval(() => { const t = new Date(); document.getElementById('toolbar-time').innerText = `${String(t.getHours()).padStart(2,0)}:${String(t.getMinutes()).padStart(2,0)}:${String(t.getSeconds()).padStart(2,0)}`; }, 1000); // 电池 function updateBattery(){ const dot = document.getElementById('batDot'); const text = document.getElementById('batText'); const ring = document.getElementById('batteryRing'); if(!navigator.getBattery){ text.textContent="不支持"; return; } function setBat(){ navigator.getBattery().then(b=>{ let per = Math.round(b.level*100); text.textContent = `${per}%`; let color = "#22c55e"; if(per<20) color="#ef4444"; else if(per<50) color="#f59e0b"; dot.style.background = color; ring.style.background = `conic-gradient(${color} ${per*3.6}deg, #e5e7eb ${per*3.6}deg)`; }); } setBat(); navigator.getBattery().then(b=>b.addEventListener("levelchange",setBat)); } updateBattery(); // 天气 // 30分钟 毫秒数 const WEATHER_CACHE_TIME = 30 * 60 * 1000; // 挂载全局缓存 window.weatherCacheData = window.weatherCacheData || null; window.weatherCacheTime = window.weatherCacheTime || 0; async function loadWeather(){ const now = Date.now(); // 未超时且有缓存,直接赋值渲染,不发请求 if(window.weatherCacheData && (now - window.weatherCacheTime < WEATHER_CACHE_TIME)){ const w = window.weatherCacheData; document.getElementById('wCity').innerText = w.city; document.getElementById('wMain').innerText = `${w.text} / ${w.temp}℃`; document.getElementById('wWind').innerText = `${w.windDir} ${w.windScale}级`; document.getElementById('wHum').innerText = `${w.humidity}%`; document.getElementById('wFeel').innerText = `${w.feelsLike}℃`; document.getElementById('wRain').innerText = w.text.includes('雨') ? '100%' : '无降雨'; return; } // 超时/无缓存 才重新请求 try { const pos = await new Promise((rs,rj)=>navigator.geolocation.getCurrentPosition(rs,rj,{timeout:8000})); const {longitude:lon,latitude:lat} = pos.coords; const geo = await (await fetch(`https://geoapi.qweather.com/v2/city/lookup?location=${lon},${lat}&key=${WEATHER_KEY}`)).json(); const nowRes = await (await fetch(`https://devapi.qweather.com/v7/weather/now?location=${geo.location[0].id}&key=${WEATHER_KEY}`)).json(); const w = nowRes.now; // 存入全局缓存 + 记录当前时间 window.weatherCacheData = { city: geo.location[0].name, text: w.text, temp: w.temp, windDir: w.windDir, windScale: w.windScale, humidity: w.humidity, feelsLike: w.feelsLike }; window.weatherCacheTime = now; // 渲染 document.getElementById('wCity').innerText = geo.location[0].name; document.getElementById('wMain').innerText = `${w.text} / ${w.temp}℃`; document.getElementById('wWind').innerText = `${w.windDir} ${w.windScale}级`; document.getElementById('wHum').innerText = `${w.humidity}%`; document.getElementById('wFeel').innerText = `${w.feelsLike}℃`; document.getElementById('wRain').innerText = w.text.includes('雨') ? '100%' : '无降雨'; } catch(e) { document.getElementById('wCity').innerText = '获取失败'; } } loadWeather(); })();