// ==UserScript== // @name 内蒙古高考招生计划添加彩色标签 // @namespace http://tampermonkey.net/ // @version 2026.6.22 // @description 【添加彩色标签后的页面仅供参考,本人不对其负责】脚本原理只是将某些文本变为彩色背景,不会改变文本内容 // @author AN drew // @match https://www.nm.zsks.cn/**/jhzy.html* // @grant GM_addStyle // @run-at document-end // ==/UserScript== (function() { 'use strict'; // ====================== 配置区 ====================== const CONFIG = { // 标签颜色 colors: { direction: '#e74c3c', // 定向 - 红色 national: '#3498db', // 国家专项计划 - 蓝色 local: '#27ae60', // 地方专项计划 - 绿色 university: '#f39c12', // 高校专项计划 - 金色 freeMedical: '#e84393', // 国家免费医学生 - 粉红 nationalTeacher: '#ad1457',// 国家公费师范生 - 紫红 localTeacher: '#9b59b6', // 地方公费师范生 - 紫色 excellentTeacher: '#2e7d32',// 优师专项 - 深绿色 military: '#215e21', // 定向培养军士生 - 深蓝灰 planSuffix: '#8e44ad', // 专业名称中的(XXX专项计划) - 紫色 teacher: '#d35400', // 专业名称中的(师范类) - 棕色 location: '#2c3e50', // 专业名称中的(定向到XX) - 深灰蓝 project: '#e67e22', // 备注中的XXX项目 - 橙色 base: '#1abc9c', // 备注中的XXX基地 - 青色 language: '#167b67', // 外语语种 - 墨绿 subject: '#00b894', // 选科要求 - 薄荷绿 }, // 匹配关键词 keywords: { direction: '定向', national: '国家专项计划', local: '地方专项计划', university: '高校专项计划', freeMedical: '国家免费医学生', nationalTeacher: '国家公费师范生', localTeacher: '地方公费师范生', excellentTeacher: '优师专项', military: '定向培养军士生', planSuffix: /([^)]*专项计划)/g, // 匹配(XXX专项计划) teacher: /(师范类)/g, // 匹配(师范类) location: /(定向到[^)]*)|定向到[^,,。]*|([^)]*[市县旗])/g, // 匹配(定向到XX) project: /[^,,。、\(;]{2,}(项目|计划)(?=[,,。]|$)/g, // 匹配XXX项目/计划 base: /[^,,。、\(;]*(? { return `.tag-${key} { background-color: ${CONFIG.colors[key]}; }`; }).join('\n'); GM_addStyle(` /* 通用标签样式 */ .tag-label { display: inline-block; padding: 1px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; line-height: 1.6; letter-spacing: 0.3px; white-space: nowrap; color: #fff; margin: 0 2px; box-shadow: 0 1px 3px rgba(0,0,0,0.12); } ${styleRules} `); GM_addStyle(` /* 让单元格内容更整齐 */ #table tbody td { vertical-align: middle; } /* 备注列允许换行,但标签不换行 */ #table tbody td:nth-child(11) .tag-label { white-space: nowrap; } /* 专业名称列(第3列)整体加粗 */ #table tbody td:nth-child(3) { font-weight: bold; text-align: left !important; } /* 该列中的标签独占一行,并保持间距 */ #table tbody td:nth-child(3) .tag-label { display: block; width: fit-content; max-width: 100%; margin: 3px 0; } `); // ====================== 工具函数 ====================== /** 获取单元格的纯文本(去除已有标签) */ function getCellText(td) { if (!td) return ''; // 克隆节点,避免影响原DOM const clone = td.cloneNode(true); // 移除所有标签(带有 .tag-label 的span) clone.querySelectorAll('.tag-label').forEach(el => el.remove()); return clone.textContent.trim(); } /** 安全设置单元格HTML,保留原有结构 */ function setCellHTML(td, html) { if (!td) return; td.innerHTML = html; } /** 对文本中的匹配项进行替换,返回HTML字符串 */ function replaceWithTag(text, regex, tagClass, tagText) { if (!text) return text; // 如果tagText未提供,使用匹配到的完整文本 return text.replace(regex, function(match) { const displayText = tagText || match; return `${displayText}`; }); } /** 处理单个单元格:根据列索引应用不同规则 */ function processCell(td, colIndex) { if (!td) return; // 如果已经包含标签,直接返回,不再处理 if (td.querySelector('.tag-label')) { return; } const rawText = getCellText(td); if (!rawText) return; let result = rawText; // ---- 列2: 专业名称 ---- if (colIndex === 2) { let modified = rawText; // 匹配 (XXX专项计划)—— planSuffix if (CONFIG.keywords.planSuffix.test(rawText)) { CONFIG.keywords.planSuffix.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.planSuffix, 'tag-planSuffix'); } // 匹配 (师范类)—— teacher if (CONFIG.keywords.teacher.test(rawText)) { CONFIG.keywords.teacher.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.teacher, 'tag-teacher'); } // 匹配(定向到XX)—— location if (CONFIG.keywords.location.test(rawText)) { CONFIG.keywords.location.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.location, 'tag-location'); } result = modified; } // ---- 列3: 计划性质 ---- else if (colIndex === 3) { if (rawText === CONFIG.keywords.direction) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.freeMedical) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.localTeacher) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.nationalTeacher) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.excellentTeacher) { result = `${rawText}`; } } // ---- 列4: 计划类别 ---- else if (colIndex === 4) { if (rawText === CONFIG.keywords.national) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.local) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.university) { result = `${rawText}`; } else if (rawText === CONFIG.keywords.military) { result = `${rawText}`; } } // ---- 列7: 外语语种 ---- else if (colIndex === 7) { if (rawText && rawText !== '不限') { const parts = rawText.match(/[^,,]+/g) || []; const cleanParts = parts.map(s => s.trim()).filter(s => s !== ''); if (cleanParts.length === 0) { result = rawText; } else if (cleanParts.length === 1) { result = `${cleanParts[0]}`; } else { const tagged = cleanParts.map(p => `${p}`).join(''); result = tagged; } } } // ---- 列9: 选科要求 ---- else if (colIndex === 9) { if (rawText && rawText !== '-' && rawText !== '不限' && rawText !== '不提科目要求') { // 使用正则匹配所有非逗号(中英文)的连续字符段 const parts = rawText.match(/[^,,]+/g) || []; const cleanParts = parts.map(s => s.trim()).filter(s => s !== ''); console.log('选科拆分:', cleanParts); if (cleanParts.length === 0) { result = rawText; } else if (cleanParts.length === 1) { result = `${cleanParts[0]}`; } else { const tagged = cleanParts.map(p => `${p}`).join(''); result = tagged; } } } // ---- 列10: 专业备注 ---- else if (colIndex === 10) { let modified = rawText; // 匹配 XXX项目 if (CONFIG.keywords.project.test(rawText)) { CONFIG.keywords.project.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.project, 'tag-project'); } // 匹配 XXX基地 if (CONFIG.keywords.base.test(rawText)) { CONFIG.keywords.base.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.base, 'tag-base'); } // 匹配 定向到XX if (CONFIG.keywords.location.test(rawText)) { CONFIG.keywords.location.lastIndex = 0; modified = replaceWithTag(modified, CONFIG.keywords.location, 'tag-location'); } result = modified; } // 只有发生变化时才更新 if (result !== rawText) { setCellHTML(td, result); } } /** 处理表格中的所有行 */ function processTable() { const table = document.querySelector('#table'); if (!table) return; const tbody = table.querySelector('tbody'); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); if (rows.length === 0) return; // 遍历每一行,处理特定列 rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length < 13) return; // 确保列数足够 // 列索引:0专业组,1代号,2名称,3性质,4类别,5学制,6学费,7语种,8口试,9选科,10备注,11地点,12计划数 // 需要处理的列:2(专业名称), 3(计划性质), 4(计划类别), 9(选科要求),10(专业备注) processCell(cells[2], 2); processCell(cells[3], 3); processCell(cells[4], 4); processCell(cells[7], 7); processCell(cells[9], 9); processCell(cells[10], 10); }); // 在所有行处理完成后,表头同步列宽(延迟确保 DOM 更新) setTimeout(syncColumnWidth, 100); } //表头同步列宽 function syncColumnWidth() { const dataTable = document.querySelector('#table'); if (!dataTable) return; const tbody = dataTable.querySelector('tbody'); if (!tbody) return; const firstRow = tbody.querySelector('tr'); if (!firstRow) return; const dataCells = firstRow.querySelectorAll('td'); if (dataCells.length === 0) return; const headerContainer = document.querySelector('.fixed-table-header'); if (!headerContainer) return; const headerRow = headerContainer.querySelector('tr'); if (!headerRow) return; const headerCells = headerRow.querySelectorAll('th'); if (headerCells.length !== dataCells.length) return; // 同步表头表格整体宽度(防止滚动条/布局差异) const headerTable = headerContainer.querySelector('table'); const dataTableRect = dataTable.getBoundingClientRect(); if (headerTable) { headerTable.style.width = dataTableRect.width + 'px'; headerTable.style.tableLayout = 'fixed'; } // 逐列设置宽度(使用 getBoundingClientRect 获取精确像素) dataCells.forEach((td, index) => { const width = td.getBoundingClientRect().width; if (width > 0) { const th = headerCells[index]; th.style.width = width + 'px'; th.style.minWidth = width + 'px'; th.style.maxWidth = width + 'px'; } }); } // ====================== 主逻辑 ====================== /** 初始化:设置MutationObserver监听表格变化 */ function init() { // 先尝试立即处理一次(如果表格已存在) const table = document.querySelector('#table'); if (table && table.querySelector('tbody tr')) { processTable(); } // 使用MutationObserver监听表格内容变化 const observer = new MutationObserver(function(mutations) { // 检查是否有新增的行或内容变化 let shouldProcess = false; for (const mutation of mutations) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { shouldProcess = true; break; } if (mutation.type === 'characterData') { shouldProcess = true; break; } } if (shouldProcess) { // 延迟执行,等待DOM完全更新 clearTimeout(window._tagTimer); window._tagTimer = setTimeout(() => { processTable(); }, 150); } }); // 观察目标:整个#table及其子树 const targetNode = document.querySelector('#table') || document.querySelector('#tablediv'); if (targetNode) { observer.observe(targetNode, { childList: true, subtree: true, characterData: true, }); } else { // 如果#table还没出现,观察整个文档 observer.observe(document.body, { childList: true, subtree: true, }); } // 额外:当搜索框输入时,延迟处理(因为搜索会重新渲染表格) const searchInput = document.querySelector('.fixed-table-toolbar .search input'); if (searchInput) { searchInput.addEventListener('input', function() { clearTimeout(window._tagTimer); window._tagTimer = setTimeout(() => { processTable(); }, 300); }); } // 也监听页面上的"上一页"链接点击(页面切换) document.addEventListener('click', function(e) { const target = e.target.closest('a'); if (target && target.id === 'syhref') { clearTimeout(window._tagTimer); window._tagTimer = setTimeout(() => { // 页面切换后,等待新表格加载 const checkInterval = setInterval(() => { const tbody = document.querySelector('#table tbody'); if (tbody && tbody.querySelector('tr')) { clearInterval(checkInterval); processTable(); } }, 200); // 最多等待5秒 setTimeout(() => clearInterval(checkInterval), 5000); }, 300); } }); // 保存observer到全局,便于调试 window.__tagObserver = observer; } // ====================== 启动 ====================== // 等待页面加载完成 if (document.readyState === 'complete' || document.readyState === 'interactive') { init(); } else { document.addEventListener('DOMContentLoaded', init); } // 额外:如果页面已经完全加载但表格还没出现,使用setInterval兜底检查 let fallbackCount = 0; const fallbackTimer = setInterval(() => { const table = document.querySelector('#table'); if (table && table.querySelector('tbody tr')) { processTable(); clearInterval(fallbackTimer); } fallbackCount++; if (fallbackCount > 30) { // 30次后停止(约15秒) clearInterval(fallbackTimer); } }, 500); console.log('[内蒙古高考招生计划添加彩色标签] 脚本已启动 ✅'); })();