增强分发系统功能,检验分发系统异常值
// ==UserScript== // @name SmartPP Helper // @namespace http://minhill.com // @version 0.4.3 // @description 增强分发系统功能,检验分发系统异常值 // @author Minhill // @include http://10.148.16.64:8080/* // @include http://10.148.16.63:8080/* // @include http://10.148.16.40:8080/* // @require https://lib.baomitu.com/dayjs/1.10.4/dayjs.min.js // @require https://lib.baomitu.com/dayjs/1.10.4/plugin/customParseFormat.min.js // @grant GM_addStyle // @supportURL https://greasyfork.org/scripts/415457 // @homepage https://greasyfork.org/zh-CN/scripts/415457 // @note 2021/03/11 增加中期表格转换功能 // ==/UserScript== (function () { 'use strict'; let config = { keyword: { CN: { tMax: '最高温度', tMin: '最低温度', fcTime: '预报时效', timeSplit: '至', hour08: '08时', hourReg: /(\d{2})时/, }, EN: { tMax: 'Maximum Temperature', tMin: 'Minimum Temperature', fcTime: 'Forecast Period', timeSplit: ' - ', hour08: '08:00', hourReg: /(\d{2}):00/, } }, dayList:['周日','周一','周二','周三','周四','周五','周六',], } /** * 工具模块 */ const utils = { /** * 创建表格 */ createTable(tableParam = { header: ['最高温', '周一', '周二'], rows: [ [ ['粤东'], ['1 - 1'], ['1 - 2'], ], [ ['粤西'], ['2 - 1'], ['2 - 2'] ], ], rowHeader: true, }) { const tableNode = document.createElement("table"); if (tableParam.header) { let headNode = tableNode.createTHead(); let newHeadRow = headNode.insertRow(-1); for (let iHeaderCell of tableParam.header) { let newTh = document.createElement('th'); let newText = document.createTextNode(iHeaderCell); newTh.appendChild(newText); newHeadRow.appendChild(newTh); } } if (tableParam.rows) { for (let iRow of tableParam.rows) { let newRow = tableNode.insertRow(-1); if (iRow.length !== 0) { let cellList; if (tableParam.rowHeader) { let newTh = document.createElement('th'); let newText = document.createTextNode(iRow[0]); newTh.appendChild(newText); newRow.appendChild(newTh); cellList = iRow.slice(1);// 去除第一个 }else{ cellList = iRow; } for (let iCell of cellList) { // let newCell = newRow.insertCell(-1); let newText = document.createTextNode(iCell); newCell.appendChild(newText); } } else { throw new Error('未正确配置单元格'); } } } else { throw new Error('未正确配置行数'); } return tableNode; }, toggle10dayTableRow(){ const tMaxTable = document.querySelector('.my-tenday-table.t-max'); if(tMaxTable){ tMaxTable.classList.toggle('only-day6'); } const tMinTable = document.querySelector('.my-tenday-table.t-min'); if(tMinTable){ tMinTable.classList.toggle('only-day6'); } }, } // var myTable = utils.createTable(tableParam); // document.body.append(myTable); /** * * @param {Object} table 表 Html DOM */ function validateTemp(table) { let IntlWords; if (table.textContent.includes('预报时效')) { // 中文 IntlWords = config.keyword.CN; } else if (table.textContent.includes('Forecast')) {// 英文 IntlWords = config.keyword.EN; } else { console.log('未识别语言'); return; } var tBody = table.children[0]; let TimeRow, TmaxRow, TminRow; for (let tr of tBody.children) {// 获取高低温行元素 if (tr.textContent.includes(IntlWords.fcTime)) TimeRow = tr;// Forecast Period if (tr.textContent.includes(IntlWords.tMax)) TmaxRow = tr;// Maximum Temperature if (tr.textContent.includes(IntlWords.tMin)) TminRow = tr;// Minimum Temperature } if (!TmaxRow) return;// 没有最高最低温直接返回 let [tMaxList, tMinList, timeList] = [[], [], []]; for (let index = 1; index < TmaxRow.children.length; index++) {// 导入高低温数据 tMinList.push(Number(TminRow.children[index].textContent)); tMaxList.push(Number(TmaxRow.children[index].textContent)); timeList.push(TimeRow.children[index].textContent); } let initHour = NaN, endHour = NaN; var timePeroidList = timeList[0].split(IntlWords.timeSplit); if (timePeroidList.length === 1) throw new Error('未识别的日期分隔符'); let initHourMatched = timePeroidList[0].match(IntlWords.hourReg); if (initHourMatched) { initHour = Number(initHourMatched[1]); } else { throw new Error('未识别的日期格式' + timePeroidList[0]); } let endHourMatched = timePeroidList[1].match(IntlWords.hourReg); if (endHourMatched) { endHour = Number(endHourMatched[1]); } else { throw new Error('未识别的日期格式' + timePeroidList[1]); } let validTmaxList, validTminList; if ((initHour === 8 && endHour === 20) || (initHour === 20 && endHour === 8)) { validTmaxList = new Array(tMaxList.length / 2); validTminList = new Array(tMaxList.length / 2); } else { console.log(`非正点时次${initHour}, ${endHour}`); return; } for (let i = 0; i < validTmaxList.length; i++) {// 判断每一对温度 if (initHour === 8 && endHour === 20) { validTmaxList[i] = tMaxList[i * 2] > tMaxList[i * 2 + 1]; validTminList[i] = tMinList[i * 2] > tMinList[i * 2 + 1]; } else if (initHour === 20 && endHour === 8) { validTmaxList[i] = tMaxList[i * 2] < tMaxList[i * 2 + 1]; validTminList[i] = tMinList[i * 2] < tMinList[i * 2 + 1]; } else { console.log(`非正点时次${initHour}, ${endHour}`); return; } } changeValidStatus(validTmaxList, TmaxRow); changeValidStatus(validTminList, TminRow); } /** * * @param {Array} validList 判断列表 * @param {Object} tempRow 温度行元素 */ function changeValidStatus(validList, tempRow) { validList.forEach((iValid, index) => { if (iValid) { tempRow.children[1 + index * 2].classList.add("valid-success"); tempRow.children[1 + index * 2 + 1].classList.add("valid-success"); tempRow.children[1 + index * 2].classList.remove("valid-error"); tempRow.children[1 + index * 2 + 1].classList.remove("valid-error"); } else { tempRow.children[1 + index * 2].classList.add("valid-error"); tempRow.children[1 + index * 2 + 1].classList.add("valid-error"); tempRow.children[1 + index * 2].classList.remove("valid-success"); tempRow.children[1 + index * 2 + 1].classList.remove("valid-success"); } }); } /** * 处理10天预报 */ function handle10daysForecast(table) { if(!table) table = document.querySelector('table.text_table3'); var tBody = table.children[0]; // 获取表头 var timeNode = document.querySelector('.forecast_time'); var timeMatch = timeNode.textContent.match(/[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))/); var timeString; if(timeMatch){ timeString = timeMatch[0]; } var headerNodes = tBody.querySelectorAll('tr:first-child th[colspan]'); var headerTimeList = []; for(let i = 0; i<headerNodes.length;i++){ let iNode = headerNodes[i]; let iText = iNode.textContent; let splitStrArr = iText.split('至'); splitStrArr = splitStrArr.map(text=>text.trim()); // let whichYear = dayjs().format('YYYY'); // let whichMonth = dayjs().format('MM'); // let whichDate = splitStrArr[1].slice(0, 2); // console.log(`${whichYear}-${whichMonth}-${whichDate}T20:00:00+08:00`); let whichDay; if(timeString){ whichDay = dayjs(timeString).add(i+1,'day').day(); }else{ whichDay = dayjs().add(i,'day').day(); } let timeObj = { text: iText, timeList: splitStrArr, day: config.dayList[whichDay], } headerTimeList.push(timeObj); } // console.log(headerTimeList); var allCols = tBody.querySelectorAll('tr.text_TR1'); // console.log(allCols); var tableList = []; for (let iCol of allCols) { let city = iCol.children[0].textContent; let dataList = []; for (let i = 0; i < 10; i++) { let Tmax = Number(iCol.children[i * 3 + 2].textContent); let Tmin = Number(iCol.children[i * 3 + 3].textContent); dataList.push({ Tmax, Tmin }); } tableList.push({ city, dataList }); } // console.log(tableList); var regionList = [{ name: '粤东', cityList: ['潮州', '饶平', '汕头', '潮阳', '澄海', '南#', '汕尾', '海丰', '陆丰', '揭阳', '普宁', '揭西', '惠来',], }, { name: '粤西', cityList: ['云浮', '郁南', '罗定', '新兴', '阳江', '阳春', '茂名', '电白', '信宜', '化州', '高州', '湛江', '遂溪', '廉江', '吴川', '雷州', '徐闻',], }, { name: '粤北', cityList: ['韶关', '新丰', '翁源', '始兴', '乳源', '南雄', '仁化', '乐昌', '清远', '连南', '连州', '连山', '阳山', '佛冈', '英德', '河源', '连平', '和平', '龙川', '紫金', '梅县', '丰顺', '平远', '兴宁', '蕉岭', '大埔', '五华',], }, { name: '珠三角', cityList: ['广州', '花都', '从化', '番禺', '增城', '##', '顺德', '三水', '高要', '封开', '德庆', '怀集', '广宁', '四会', '东莞', '深圳', '惠阳', '龙门', '博罗', '惠东', '珠海', '斗门', '中山', '新会', '上川', '鹤山', '开平', '恩平', '台山'], }, ]; for (let iRegion of regionList) {// 归类 iRegion.dataList = iRegion.cityList.map(iCity => { let foundCity = tableList.find(iCol => { // console.log(iCol.city); return iCol.city.indexOf(iCity.trim()) !== -1 }); // console.log(foundCity); if (!foundCity) { alert('未找到需要的市县: ' + iCity); throw new Error('未找到需要的市县: ' + iCity); } else { return foundCity; } }) }; var fcList10 = regionList.map(iRegion => { const cityCount = iRegion.cityList.length; let TmaxList = new Array(10).fill(0); let TminList = new Array(10).fill(0); for (let iCity = 0; iCity < cityCount; iCity++) {// 求和 let iTmaxList = iRegion.dataList[iCity].dataList.map(v => v.Tmax); TmaxList = TmaxList.map((v, i) => v + iTmaxList[i]); let iTminList = iRegion.dataList[iCity].dataList.map(v => v.Tmin); TminList = TminList.map((v, i) => v + iTminList[i]); } TmaxList = TmaxList.map(v => v / cityCount); TminList = TminList.map(v => v / cityCount); return { name: iRegion.name, TmaxList, TminList, } }); fcList10.time = headerTimeList; console.log(fcList10); var tableTmaxData = { header:['最高气温'].concat(headerTimeList.map(iTime=>iTime.day)), rows: fcList10.map(region=>{ let TmaxStrList = region.TmaxList.map(v=>v.toFixed(1)); return [region.name,].concat(TmaxStrList); }), rowHeader: true, } // 判断overlay 是否生成 let overlayCreated = document.querySelector('.my-overlay'); let overlay; if(overlayCreated){ overlay = overlayCreated; overlay.innerHTML = ''; }else{ overlay = document.createElement('div'); overlay.setAttribute('class', 'my-overlay hidden'); document.body.append(overlay); } // 表格切换按钮 var hiddenRowBtn = document.createElement('button'); hiddenRowBtn.textContent = '切换表格显示(10天/后6天)'; hiddenRowBtn.addEventListener('click', utils.toggle10dayTableRow); overlay.appendChild(hiddenRowBtn); // 最高气温 var tableTmax = utils.createTable(tableTmaxData); // overlay.setAttribute('class', 'my-overlay hidden'); tableTmax.setAttribute('class', 'my-tenday-table t-max'); overlay.appendChild(tableTmax); // document.body.append(overlay); // 最低气温 var tableTminData = { header:['最低气温'].concat(headerTimeList.map(iTime=>iTime.day)), rows: fcList10.map(region=>{ let TminStrList = region.TminList.map(v=>v.toFixed(1)); return [region.name,].concat(TminStrList); }), rowHeader: true, } var tableTmin = utils.createTable(tableTminData); tableTmin.setAttribute('class', 'my-tenday-table t-min'); overlay.appendChild(tableTmin); } GM_addStyle( ` .hidden{ display:none; } .my-btn-overlay{ z-index: 10; position: fixed; right: 120px; top: 185px; } .my-overlay{ z-index: 10; position: fixed; left: 50%; right: 50%; top: 0px; left: 0px; right: 0px; margin-left: auto; margin-right: auto; width: 800px; background: white; } .my-tenday-table { margin-top:20px; border-collapse: collapse; border-spacing: 0; empty-cells: show; border: 1px solid #cbcbcb; } .my-tenday-table caption { color: #000; font: italic 85%/1 arial,sans-serif; padding: 1em 0; text-align: center; } .my-tenday-table td,.my-tenday-table th { border: 1px solid #cbcbcb; border-width: 1px; font-size: inherit; margin: 0; overflow: visible; padding: .5em 1em; } .my-tenday-table thead { color: #000; text-align: left; vertical-align: bottom; } .my-tenday-table td { background-color: transparent; } .my-tenday-table.only-day6 td:nth-child(2), .my-tenday-table.only-day6 td:nth-child(3), .my-tenday-table.only-day6 td:nth-child(4), .my-tenday-table.only-day6 td:nth-child(5){ display: none; } .my-tenday-table.only-day6 tr:first-child th:nth-child(2), .my-tenday-table.only-day6 tr:first-child th:nth-child(3), .my-tenday-table.only-day6 tr:first-child th:nth-child(4), .my-tenday-table.only-day6 tr:first-child th:nth-child(5){ display: none; } ` ) /** * 获取表格 */ function tableSelector() { var tableList = document.querySelectorAll('table.text_table2'); for (let iTable of tableList) { validateTemp(iTable); } // console.log('页面变动监测'); let myBtnOverlay = document.querySelector('.my-btn-overlay'); let fcTitle = document.querySelector('.forecast_title'); // console.log(fcTitle); if (fcTitle && fcTitle.textContent.indexOf('10天天气预报') !== -1) { // console.log('10天预报'); let table3 = document.querySelector('table.text_table3'); if (table3){ handle10daysForecast(table3); if(myBtnOverlay){ // 有按钮显示 myBtnOverlay.classList.remove('hidden'); }else{ createFloatBtn();// 没有按钮创建 } } }else{ if(myBtnOverlay){ myBtnOverlay.classList.add('hidden'); } let myOverlay = document.querySelector('.my-overlay'); myOverlay && myOverlay.classList.add('hidden'); } } /** * 创建浮动按钮 */ function createFloatBtn() { let btnOverlay = document.createElement('div'); btnOverlay.setAttribute('class', 'my-btn-overlay'); let tenDayBtn = document.createElement('button'); tenDayBtn.addEventListener('click', ()=>{ let tenDayOverlay = document.querySelector('.my-overlay'); if(tenDayOverlay){ tenDayOverlay.classList.toggle('hidden'); } }); tenDayBtn.textContent = '分片统计表'; btnOverlay.appendChild(tenDayBtn); document.body.append(btnOverlay); } /** * 模板部分监听器 */ function templateObserver() { const targetNode = document.getElementById('templatePreContent');// 选择需要观察变动的节点 const config = { childList: true, subtree: false };// 观察器的配置(需要观察什么变动) const callback = function (mutationsList, observer) {// 当观察到变动时执行的回调函数 tableSelector(); }; const observer = new MutationObserver(callback);// 创建一个观察器实例并传入回调函数 observer.observe(targetNode, config);// 以上述配置开始观察目标节点 return tableSelector(); } /** * 主入口函数 */ function main() { var style = '<style>.valid-success{background-color:lightgreen;}.valid-error{background-color:orange;}</style>'; var ele = document.createElement('div'); ele.innerHTML = style; document.getElementsByTagName('head')[0].appendChild(ele.firstElementChild); lookUpTarget(); } /** * 处理注入问题 */ function lookUpTarget() { const targetNode = document.getElementById('templatePreContent'); if (targetNode) { return templateObserver() } else { console.log('未找到目标元素,等待5秒重新查找'); return setTimeout(lookUpTarget, 5000); } } main(); })();