// ==UserScript== // @name 手势标注校验工具 - 分组检查 // @namespace http://113.207.49.90/ // @version 1.3.0 // @description 在标注平台审核/质检工序中自动校验半身框/手势框分组问题 // @author 粟镇 // @match http://113.207.49.90:8080/w/task.html* // @match http://113.207.49.90:8050/w/task.html* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; // ============================================================ // 配置区 // ============================================================ const CHECK_CLASSES = ['half_bbox_xywh', 'handbboxxywh']; let cachedData = null; let panel = null; let panelReady = false; // ============================================================ // 拦截平台的 get-mark-data 请求 // ============================================================ function hookXHR() { const origOpen = XMLHttpRequest.prototype.open; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this._szUrl = url; return origOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (body) { const self = this; const origOnReady = this.onreadystatechange; this.onreadystatechange = function () { if (self.readyState === 4 && self.status === 200) { if (self._szUrl && self._szUrl.indexOf('get-mark-data') !== -1) { try { const resp = JSON.parse(self.responseText); if (resp.code === 0 && resp.data) { cachedData = resp.data; if (panelReady) { autoRunCheck(); } } } catch (e) {} } } if (origOnReady) { return origOnReady.apply(this, arguments); } }; return origSend.apply(this, arguments); }; } // ============================================================ // 注入CSS // ============================================================ function injectStyles() { if (document.getElementById('sz-check-styles')) return; const style = document.createElement('style'); style.id = 'sz-check-styles'; style.textContent = ` #sz-check-panel { position: fixed; top: 80px; left: 320px; width: 340px; max-height: 70vh; background: linear-gradient(145deg, #1a1a2e 0%, #16213e 100%); color: #e0e0e0; border: 1px solid rgba(233,69,96,0.3); border-radius: 12px; z-index: 999999; font-family: "Microsoft YaHei", "PingFang SC", sans-serif; font-size: 13px; box-shadow: 0 8px 32px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05); overflow: hidden; transition: box-shadow 0.2s; } #sz-check-panel:hover { box-shadow: 0 8px 32px rgba(0,0,0,0.6), 0 0 20px rgba(233,69,96,0.15); } #sz-check-header { background: linear-gradient(135deg, #0f3460 0%, #1a1a40 100%); padding: 10px 14px; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; border-bottom: 1px solid rgba(233,69,96,0.2); } #sz-check-header:active { cursor: grabbing; } .sz-title { font-weight: bold; font-size: 14px; color: #e94560; text-shadow: 0 0 10px rgba(233,69,96,0.3); display: flex; align-items: center; gap: 6px; } .sz-title-dot { width: 8px; height: 8px; background: #e94560; border-radius: 50%; box-shadow: 0 0 8px #e94560; animation: sz-pulse 2s ease-in-out infinite; } @keyframes sz-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } .sz-btn-check { background: linear-gradient(135deg, #e94560 0%, #c73e54 100%); color: #fff; border: none; padding: 5px 14px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: bold; transition: all 0.2s; box-shadow: 0 2px 8px rgba(233,69,96,0.3); } .sz-btn-check:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(233,69,96,0.5); } .sz-btn-check:active { transform: translateY(0); } .sz-btn-check:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .sz-btn-toggle { background: rgba(255,255,255,0.08); color: #aaa; border: 1px solid rgba(255,255,255,0.1); width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; margin-left: 6px; } .sz-btn-toggle:hover { background: rgba(255,255,255,0.15); color: #fff; } #sz-check-content { padding: 12px 14px; max-height: calc(70vh - 50px); overflow-y: auto; } #sz-check-content::-webkit-scrollbar { width: 6px; } #sz-check-content::-webkit-scrollbar-track { background: transparent; } #sz-check-content::-webkit-scrollbar-thumb { background: rgba(233,69,96,0.3); border-radius: 3px; } #sz-check-content::-webkit-scrollbar-thumb:hover { background: rgba(233,69,96,0.5); } .sz-stats { margin-bottom: 10px; padding: 10px 12px; background: rgba(15,52,96,0.4); border-radius: 8px; border: 1px solid rgba(255,255,255,0.05); } .sz-stats-title { color: #e94560; font-weight: bold; margin-bottom: 6px; font-size: 12px; text-transform: uppercase; letter-spacing: 1px; } .sz-stats-body { color: #8892b0; font-size: 12px; line-height: 1.8; } .sz-badge { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 11px; font-weight: bold; } .sz-badge-ok { background: rgba(78,204,163,0.15); color: #4ecca3; border: 1px solid rgba(78,204,163,0.3); } .sz-badge-warn { background: rgba(255,165,0,0.15); color: #ffa500; border: 1px solid rgba(255,165,0,0.3); } .sz-pass { color: #4ecca3; text-align: center; padding: 24px 0; font-size: 15px; font-weight: bold; } .sz-warn-header { color: #e94560; font-weight: bold; margin: 10px 0 6px; font-size: 13px; display: flex; align-items: center; gap: 4px; } .sz-warn-item { background: rgba(233,69,96,0.08); border-left: 3px solid #e94560; padding: 8px 10px; margin-bottom: 5px; border-radius: 0 6px 6px 0; font-size: 12px; line-height: 1.6; color: #ccc; transition: background 0.2s; } .sz-warn-item:hover { background: rgba(233,69,96,0.15); } .sz-info { color: #8892b0; padding: 8px 0; text-align: center; } .sz-loading { color: #8892b0; text-align: center; padding: 24px 0; } `; document.head.appendChild(style); } // ============================================================ // 创建面板 // ============================================================ function createPanel() { if (panel && document.body.contains(panel)) return panel; injectStyles(); panel = document.createElement('div'); panel.id = 'sz-check-panel'; panel.innerHTML = `