您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds vibrant row hover effect (overriding all cell colors), conditional status cell styling for test status and sample status, and static red background for emergency rows in AG Grid.
当前为
// ==UserScript== // @name Enhanced AG Grid Functionality // @version 4.3 // @description Adds vibrant row hover effect (overriding all cell colors), conditional status cell styling for test status and sample status, and static red background for emergency rows in AG Grid. // @match https://his.kaauh.org/lab/* // @author Hamad AlShegifi // @grant GM_addStyle // @namespace http://tampermonkey.net/ // ==/UserScript== (function () { 'use strict'; // Inject CSS for hover effect, status cell styling, and emergency highlighting GM_addStyle(` .ag-row { transition: background-color 0.3s ease; } .vibrant-hover { /* Background color and text color are now applied to cells within the hovered row */ } .ag-row.vibrant-hover .ag-cell { background-color: #87dced !important; /* Light blue color */ color: black !important; /* Black text color */ font-weight: bold !important; /* Bold font */ } .ag-cell[col-id="testStatus"].status-verified-level2, .ag-cell[col-id="testStatus"].status-verified-level1 { background-color: #90EE90 !important; /* Light green color */ } .ag-cell[col-id="testStatus"].status-ordered { background-color: #FFFFE0 !important; /* Light yellow color */ } .ag-cell[col-id="testStatus"].status-resulted { background-color: #FFA500 !important; /* Orange color */ color: black !important; /* Ensure text is readable on orange */ } .emergency-row { background-color: #ffe0e0 !important; /* Light red */ } .ag-cell[col-id="sampleStatus"].status-received { background-color: #90EE90 !important; /* Light green color for Received sample status */ } `); // List of col-id attributes to target for hover effect const targetColumnIdsForHover = [ "orderNo", "testId", "testDescription", "clusterMrn", "hospitalMrn", "patientName", "dob", "nationalIqamaId", "department", "clinic", "doctor", "analyzer", "orderDateAndTime", "lastUpdatedDate", "sampleStatus", "referenceLab", "accessionNo", "barcode", "sequenceNo", "primaryPatientId", "referenceLabDesc", "testStatus", "orderLastModifiedOnEpoch", "orderCreatedOnEpoch", "equipmentName", "doctorName", "localMrn", "dateOfBirth", "idNumber" ]; const columnsToUncheck = [ 'Lab Order No', 'Hospital MRN', 'DOB', 'Test ID', 'National/Iqama Id', 'Department', 'Doctor', 'Analyzer', 'Reference Lab', 'Accession No', 'Sequence No','Age','Container Type','Storage Condition' ]; let hasRunOnce = false; // Prevents running the column unchecking code more than once function isSpecificPage() { return window.location.href.endsWith('/#/lab-orders/lab-test-analyzer'); } function areColumnsChecked() { return columnsToUncheck.some(column => isColumnChecked(column)); } function isColumnChecked(labelText) { const labels = document.querySelectorAll('.ag-column-tool-panel-column-label'); for (const label of labels) { if (label.textContent.trim() === labelText) { const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked'); if (checkbox) return true; // Column is checked } } return false; // Column is not checked } function ensureColumnsUnchecked() { if (hasRunOnce || !isSpecificPage()) return; if (!areColumnsChecked()) return; // Do nothing if columns are not checked hasRunOnce = true; console.log("Unchecking checked columns..."); setTimeout(() => { columnsToUncheck.forEach(column => clickColumnLabel(column)); }, 1000); } function ensureOtherColumnsChecked() { if (!isSpecificPage()) return; console.log("Ensuring all other columns are checked..."); const allLabels = document.querySelectorAll('.ag-column-tool-panel-column-label'); allLabels.forEach(label => { const labelText = label.textContent.trim(); if (!columnsToUncheck.includes(labelText)) { const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-unchecked'); if (checkbox) { label.click(); // Click to check the column if unchecked } } }); } function clickColumnLabel(labelText) { if (!isSpecificPage()) return; const labels = document.querySelectorAll('.ag-column-tool-panel-column-label'); labels.forEach(label => { if (label.textContent.trim() === labelText) { const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked'); if (checkbox) { label.click(); // Click only if checked } } }); } function highlightEmergencyRows() { const rows = document.querySelectorAll('div[role="row"]'); rows.forEach(row => { const clinicCell = row.querySelector('div[col-id="clinic"]'); if (clinicCell && clinicCell.textContent.trim() === 'EMERGENCY') { row.classList.add('emergency-row'); } else { row.classList.remove('emergency-row'); // Remove the class if the condition is no longer met } }); } function initColumnToggle() { if (!isSpecificPage()) return; console.log("Checking if columns need to be unchecked and highlighting emergency rows..."); let attempts = 0; const interval = setInterval(() => { if (document.querySelector('.ag-side-buttons')) { ensureColumnsUnchecked(); ensureOtherColumnsChecked(); highlightEmergencyRows(); // Call the highlighting function here clearInterval(interval); } if (++attempts > 10) { highlightEmergencyRows(); // Ensure highlighting runs even if sidebar isn't found quickly clearInterval(interval); } }, 500); // Also run highlighting on initial load attempt highlightEmergencyRows(); } // Function to apply hover effects to the entire row when any target cell is hovered function applyHoverEffect() { document.querySelectorAll('.ag-cell').forEach(cell => { const colId = cell.getAttribute('col-id'); if (colId && targetColumnIdsForHover.includes(colId)) { const row = cell.closest('.ag-row'); // Find the parent row if (row) { // Add hover effect to the entire row on mouseenter cell.addEventListener('mouseenter', () => { row.classList.add('vibrant-hover'); // Add hover effect to the row }); // Remove hover effect on mouseleave cell.addEventListener('mouseleave', () => { row.classList.remove('vibrant-hover'); // Remove hover effect from the row }); } } }); } // Function to apply conditional background color to the "Status" column function applyStatusCellStyle() { document.querySelectorAll('.ag-cell[col-id="testStatus"]').forEach(cell => { const statusValue = cell.textContent.trim(); cell.classList.remove('status-verified-level2', 'status-verified-level1', 'status-ordered', 'status-resulted'); // Remove previous styles if (statusValue === "VerifiedLevel2" || statusValue === "VerifiedLevel1") { cell.classList.add('status-verified-level2'); } else if (statusValue === "Ordered") { cell.classList.add('status-ordered'); } else if (statusValue === "Resulted") { cell.classList.add('status-resulted'); } }); // Apply style for Sample Status = Received document.querySelectorAll('.ag-cell[col-id="sampleStatus"]').forEach(cell => { if (cell.textContent.trim() === "Received") { cell.classList.add('status-received'); } else { cell.classList.remove('status-received'); // Ensure the class is removed if the value changes } }); } const observer = new MutationObserver(() => { applyHoverEffect(); applyStatusCellStyle(); highlightEmergencyRows(); // Call highlightEmergencyRows on every DOM change if (isSpecificPage()) { hasRunOnce = false; // Reset run flag for repeated executions initColumnToggle(); } }); // **Important: Observing the correct AG Grid body viewport** const gridBodyViewport = document.querySelector('.ag-body-viewport'); // Observe the grid body viewport if (gridBodyViewport) { observer.observe(gridBodyViewport, { childList: true, subtree: false }); // subtree should be false here as we are observing direct children (rows) } else { console.warn("AG Grid body viewport not found. Falling back to body observer."); observer.observe(document.body, { childList: true, subtree: true }); } window.addEventListener('load', () => { applyHoverEffect(); applyStatusCellStyle(); if (isSpecificPage()) { initColumnToggle(); } }); // Function to set the dropdown value (keeping this separate as it seems independent) (function() { 'use strict'; // Function to set the dropdown value function setDropdownValue() { const dropdown = document.getElementById("dropdownPaginationPageSize"); if (dropdown && dropdown.value !== "100") { dropdown.value = "100"; // Set the value to "100" // Trigger the 'change' event const event = new Event('change', { bubbles: true }); dropdown.dispatchEvent(event); console.log("Dropdown value set to 100"); } } // Function to observe changes in the DOM for the dropdown function observeDOMForDropdown() { const observer = new MutationObserver(() => { setDropdownValue(); // Check and set the dropdown value when changes are detected }); // Observe the entire document for changes observer.observe(document.body, { childList: true, subtree: true, }); console.log("MutationObserver is active for dropdown"); } // Run the function when the page is fully loaded window.addEventListener('load', () => { setDropdownValue(); // Initial check observeDOMForDropdown(); // Start observing for dynamic changes }); })(); })(); (function() { 'use strict'; // Add CSS styles GM_addStyle(` #ordered-tests-tooltip { position: absolute; bottom: -10px; left: 0; transform: translateY(100%); background: white; border: 1px solid #e0e0e0; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10000; font-family: 'Segoe UI', Arial, sans-serif; display: none; pointer-events: none; transition: opacity 0.2s ease; white-space: nowrap; max-width: none; overflow: visible; } #ordered-tests-tooltip { /* existing styles */ min-width: 300px; } .tooltip-header.resulted-header { background: #f0fff0; border-bottom: 1px solid #d0ffd0; color: #2e7d32; } .test-item.completed { color: #2e7d32; } .test-item.completed .test-icon { color: #4caf50; font-weight: bold; margin-right: 8px; } .test-item.pending { color: #d32f2f; } .no-tests { padding: 8px 0; color: #666; font-style: italic; text-align: center; } .verify1-btn:hover #ordered-tests-tooltip { display: block; opacity: 1; } .tooltip-header { background: #fff1f1; border-bottom: 1px solid #ffd6d6; padding: 12px 15px; font-weight: 600; color: #d32f2f; display: flex; align-items: center; gap: 10px; white-space: nowrap; } .tooltip-content { padding: 10px 15px; white-space: nowrap; } .test-item { display: flex; align-items: center; gap: 6px; margin: 2px 0; font-size: 13px; } .test-item:last-child { border-bottom: none; } .test-bullet { color: #ff5252; font-size: 14px; } .tooltip-footer { background: #f9f9f9; border-top: 1px solid #eee; padding: 8px 15px; font-size: 11px; color: #757575; text-align: right; white-space: nowrap; } .no-ordered-tests { padding: 15px; text-align: center; color: #666; font-style: italic; background: #f8faf8; white-space: nowrap; } `); // Configuration const CONFIG = { POLL_INTERVAL: 1000, MAX_WAIT_TIME: 30000, SCAN_INTERVAL: 3000, COLUMN_PATTERNS: { TEST_DESC: ['desc', 'testdesc', 'name'], STATUS: ['status', 'resultstatus', 'state'] } }; // Create the tooltip element function createTooltip() { const tooltip = document.createElement('div'); tooltip.id = 'ordered-tests-tooltip'; return tooltip; } // Find cells by column patterns function findCellByPatterns(row, patterns) { const cells = row.querySelectorAll('[col-id]'); for (const cell of cells) { const colId = cell.getAttribute('col-id')?.toLowerCase(); if (patterns.some(pattern => colId?.includes(pattern))) { return cell; } } return null; } // Get all tests with "Ordered" status function getOrderedTests() { const leftRows = Array.from(document.querySelectorAll('.ag-pinned-left-cols-container .ag-row')); const centerRows = Array.from(document.querySelectorAll('.ag-center-cols-container .ag-row')); const rightRows = Array.from(document.querySelectorAll('.ag-pinned-right-cols-container .ag-row')); const orderedTests = []; for (let i = 0; i < leftRows.length; i++) { const leftRow = leftRows[i]; const centerRow = centerRows[i] || {}; const rightRow = rightRows[i] || {}; const testDescCell = findCellByPatterns(leftRow, CONFIG.COLUMN_PATTERNS.TEST_DESC); const statusCell = findCellByPatterns(rightRow, CONFIG.COLUMN_PATTERNS.STATUS) || findCellByPatterns(centerRow, CONFIG.COLUMN_PATTERNS.STATUS); const testName = testDescCell?.textContent?.trim(); const status = statusCell?.textContent?.trim().toLowerCase() || ''; if (testName && status.includes('ordered')) { orderedTests.push(testName); } } return orderedTests; } // Update tooltip content with ordered tests (sorted alphabetically) // Update the tooltip content generation function function updateTooltipContent(tooltip) { const now = new Date(); const timeString = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); const dateString = now.toLocaleDateString([], {month: 'short', day: 'numeric'}); // Get all tests and categorize them const { orderedTests, resultedTests } = getAllTests(); // Create a Set to remove duplicates, then sort alphabetically (case-insensitive) const uniqueOrderedTests = [...new Set(orderedTests)].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}) ); const uniqueResultedTests = [...new Set(resultedTests)].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}) ); tooltip.innerHTML = ` <div class="tooltip-header"> <span>⏳ Pending Tests (${uniqueOrderedTests.length})</span> </div> <div class="tooltip-content"> ${uniqueOrderedTests.length > 0 ? uniqueOrderedTests.map(test => ` <div class="test-item pending"> <span class="test-bullet">•</span> <span>${test}</span> </div> `).join('') : ` <div class="no-tests">No pending tests</div>` } </div> <div class="tooltip-header resulted-header"> <span>✅ Tests ready for verification (${uniqueResultedTests.length})</span> </div> <div class="tooltip-content"> ${uniqueResultedTests.length > 0 ? uniqueResultedTests.map(test => ` <div class="test-item completed"> <span class="test-icon">✓</span> <span>${test}</span> </div> `).join('') : ` <div class="no-tests">No resulted tests</div>` } </div> <div class="tooltip-footer"> Updated: ${dateString} ${timeString} </div> `;} // Function to get all tests and categorize them function getAllTests() { const leftRows = Array.from(document.querySelectorAll('.ag-pinned-left-cols-container .ag-row')); const centerRows = Array.from(document.querySelectorAll('.ag-center-cols-container .ag-row')); const rightRows = Array.from(document.querySelectorAll('.ag-pinned-right-cols-container .ag-row')); const orderedTests = []; const resultedTests = []; for (let i = 0; i < leftRows.length; i++) { const leftRow = leftRows[i]; const centerRow = centerRows[i] || {}; const rightRow = rightRows[i] || {}; const testDescCell = findCellByPatterns(leftRow, CONFIG.COLUMN_PATTERNS.TEST_DESC); const statusCell = findCellByPatterns(rightRow, CONFIG.COLUMN_PATTERNS.STATUS) || findCellByPatterns(centerRow, CONFIG.COLUMN_PATTERNS.STATUS); const testName = testDescCell?.textContent?.trim(); const status = statusCell?.textContent?.trim().toLowerCase() || ''; if (testName) { if (status.includes('ordered')) { orderedTests.push(testName); } else if (status.includes('resulted')) { resultedTests.push(testName); } } } return { orderedTests, resultedTests }; } // Main function to attach tooltip to VERIFY1 button function attachTooltipToVerifyButton() { const verifyButton = document.querySelector('.verify1-btn'); if (!verifyButton) return false; if (verifyButton.querySelector('#ordered-tests-tooltip')) return true; const tooltip = createTooltip(); verifyButton.style.position = 'relative'; verifyButton.appendChild(tooltip); verifyButton.addEventListener('mouseenter', () => { updateTooltipContent(tooltip); tooltip.style.display = 'block'; }); verifyButton.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; }); return true; } // Wait for button to appear function waitForButton() { const startTime = Date.now(); const checkInterval = setInterval(() => { if (attachTooltipToVerifyButton() || Date.now() - startTime > CONFIG.MAX_WAIT_TIME) { clearInterval(checkInterval); } }, CONFIG.POLL_INTERVAL); } // Start the script if (document.readyState === 'complete') { waitForButton(); } else { window.addEventListener('load', waitForButton); } // Watch for SPA navigation const observer = new MutationObserver(() => { if (!document.querySelector('.verify1-btn #ordered-tests-tooltip')) { attachTooltipToVerifyButton(); } }); observer.observe(document.body, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址