// ==UserScript== // @name 车位点查验(新系统) // @namespace http://tampermonkey.net/ // @version 1.4 // @description 拦截暂存请求载荷,校验几何坐标点数是否为4(仅显示失败项) // @author You // @match *://9.20.7.7:8060/* // @grant GM_addStyle // @connect 9.20.7.7 // @run-at document-end // ==/UserScript== (function() { 'use strict'; // ============ 配置区域 ============ const CONFIG = { selectors: { submitBtn: '#app > div > div > div.tag-content.header-box > div:nth-child(2) > div > div.btn.submit', tempSaveBtn: '#app > div > div > div.content-box > div.sidebar-wrap.sidebar > div > div.tool-list > li:nth-child(20) > div', container: '#app' }, targetApi: '/api/sk-task/mark/v3/mgt/marker-temp-save', verifyBtn: { id: 'custom-verify-btn', text: '校验坐标', style: 'width:110px;background:#2196F3;color:#fff;border:none;border-radius:4px;cursor:pointer;margin-right:10px;font-size:14px;text-align:center;line-height:32px;' }, expectedCoordCount: 4 }; let state = { isMonitoring: false, originalSubmitBtn: null, xhrBackup: null }; // ============ 工具函数 ============ function verifySubmitBtnText(btn) { const text = btn?.innerText?.trim(); return text === '提交(Ctrl+Alt)'; } function verifyTempSaveBtnTitle(btn) { const title = btn?.title; return title === '暂存(Ctrl+S)'; } function createVerifyButton() { if (document.getElementById(CONFIG.verifyBtn.id)) return null; const btn = document.createElement('div'); btn.id = CONFIG.verifyBtn.id; btn.className = 'btn submit'; btn.setAttribute('role', 'button'); btn.style.cssText = CONFIG.verifyBtn.style; btn.textContent = CONFIG.verifyBtn.text; btn.onclick = onVerifyClick; btn.onmouseenter = (e) => e.target.style.background = '#1976D2'; btn.onmouseleave = (e) => e.target.style.background = '#2196F3'; return btn; } function onVerifyClick() { state.isMonitoring = true; setupXHRInterceptor(); const tempSaveBtn = document.querySelector(CONFIG.selectors.tempSaveBtn); if (tempSaveBtn) { tempSaveBtn.click(); } else { resetMonitoring(); alert('未找到暂存按钮,请确认页面结构'); } } function setupXHRInterceptor() { state.xhrBackup = { open: window.XMLHttpRequest.prototype.open, send: window.XMLHttpRequest.prototype.send }; window.XMLHttpRequest.prototype.open = function(method, url, ...args) { this._targetUrl = url; this._method = method; return state.xhrBackup.open.apply(this, [method, url, ...args]); }; window.XMLHttpRequest.prototype.send = function(body) { const xhr = this; if (state.isMonitoring && xhr._targetUrl?.includes(CONFIG.targetApi)) { try { let payload = body; if (body instanceof Blob) { const reader = new FileReader(); reader.onload = () => processPayload(reader.result); reader.onerror = () => resetMonitoring(); reader.readAsText(body); return state.xhrBackup.send.apply(xhr, arguments); } if (body instanceof FormData) { const obj = {}; for (let [k, v] of body.entries()) obj[k] = v; payload = JSON.stringify(obj); } if (typeof payload !== 'string' && payload !== null) { payload = JSON.stringify(payload); } processPayload(payload); } catch (e) { resetMonitoring(); } } return state.xhrBackup.send.apply(xhr, arguments); }; } /** 处理请求载荷并校验坐标 - 仅显示不通过的项 */ function processPayload(payloadStr) { try { const requestData = JSON.parse(payloadStr); let markResult = requestData.markResult; if (typeof markResult === 'string') { markResult = JSON.parse(markResult).markResult; } if (!markResult?.features || !Array.isArray(markResult.features)) { throw new Error('markResult.features 不存在或格式错误'); } const failedResults = []; // 🎯 只收集失败的项 let allValid = true; markResult.features.forEach((feature, idx) => { const coords = feature.geometry?.coordinates || []; const coordCount = coords[0].length - 1; const tagId = feature.properties?.formData?.id || `tag-${idx + 1}`; const isValid = coordCount === CONFIG.expectedCoordCount; if (!isValid) { allValid = false; // 🎯 仅推送失败项到结果数组 failedResults.push({ '序号': idx + 1, '标签ID': tagId, '坐标点数(length-1)': coordCount, '期望值': CONFIG.expectedCoordCount, '状态': '❌ 失败' }); } }); // 🎯 仅当有失败项时输出表格 if (failedResults.length > 0) { console.group('📊 坐标校验失败项'); console.table(failedResults); console.groupEnd(); alert(`⚠️ 发现 ${failedResults.length} 个坐标异常标签,请查看控制台表格详情`); } else { // 全部通过,静默切换按钮 toggleButtons(true); console.log("✅ 全部通过") } } catch (e) { alert('校验解析失败: ' + e.message); resetMonitoring(); } } function toggleButtons(showSubmit) { const verifyBtn = document.getElementById(CONFIG.verifyBtn.id); const submitBtn = state.originalSubmitBtn; if (showSubmit) { verifyBtn && (verifyBtn.style.display = 'none'); submitBtn && (submitBtn.style.display = ''); } else { verifyBtn && (verifyBtn.style.display = ''); submitBtn && (submitBtn.style.display = 'none'); } } function resetMonitoring() { state.isMonitoring = false; if (state.xhrBackup) { window.XMLHttpRequest.prototype.open = state.xhrBackup.open; window.XMLHttpRequest.prototype.send = state.xhrBackup.send; state.xhrBackup = null; } } function initButtonReplace() { const submitBtn = document.querySelector(CONFIG.selectors.submitBtn); const tempSaveBtn = document.querySelector(CONFIG.selectors.tempSaveBtn); if (!submitBtn || !tempSaveBtn) return false; if (!verifySubmitBtnText(submitBtn)) return false; if (!verifyTempSaveBtnTitle(tempSaveBtn)) return false; if (document.getElementById(CONFIG.verifyBtn.id)) return true; state.originalSubmitBtn = submitBtn; submitBtn.style.display = 'none'; const verifyBtn = createVerifyButton(); if (verifyBtn && submitBtn.parentNode) { submitBtn.parentNode.insertBefore(verifyBtn, submitBtn); } return true; } function observeDOM() { const target = document.querySelector(CONFIG.selectors.container) || document.body; const observer = new MutationObserver((mutations) => { const submitBtn = document.querySelector(CONFIG.selectors.submitBtn); if (submitBtn && !state.originalSubmitBtn) { if (initButtonReplace()) { observer.disconnect(); } } }); observer.observe(target, { childList: true, subtree: true }); } function init() { GM_addStyle(` #${CONFIG.verifyBtn.id}:hover { opacity: 0.9; } #${CONFIG.verifyBtn.id}:active { transform: scale(0.98); } `); if (!initButtonReplace()) { observeDOM(); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();