【油猴脚本】快捷展现省内公文主要领导后续提交人员

处理省内OA文件,快速展现主要领导提交的后续人员列表,避免重复提交。

// ==UserScript==
// @name         省内OA流程跟踪提取XXX送办理后续人员(隐藏iframe+通信版)
// @namespace    http://tampermonkey.net/
// @version      1.15.0
// @description  无感后台加载流程跟踪页提取后续人员,保留原iframe逻辑与通信机制,手动点击仍可正常显示。
// @author       subk
// @match        http://XXXXXX/newWorkSheet/*
// @match        http://XXXXXX/OADocument/document/*
// @match        http://XXXXXX/mocha.component.processtrack.v5/process/processinfo*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
    'use strict';

    const TARGET_HANDLER_NAME = "XXX";
    const ACTION_KEYWORD = "送办理";

    // ================= 主页面逻辑 =================
    if (location.pathname.includes('/newWorkSheet/') ||
        location.pathname.includes('/OADocument/document/')) {

        function waitForElement(selector, timeout = 15000) {
            return new Promise((resolve, reject) => {
                const element = document.querySelector(selector);
                if (element) { resolve(element); return; }
                const observer = new MutationObserver(() => {
                    const el = document.querySelector(selector);
                    if (el) { observer.disconnect(); resolve(el); }
                });
                observer.observe(document.body, { childList: true, subtree: true });
                if (timeout > 0) {
                    setTimeout(() => { observer.disconnect(); reject(new Error(`Timeout waiting for ${selector}`)); }, timeout);
                }
            });
        }

        async function openHiddenProcessTrack() {
            try {
                const trackButton = await waitForElement('button[data-action="trace"]', 10000);

                let trackUrl = trackButton.getAttribute('data-url');

                if (!trackUrl) {
                    // 模拟点击获取真实URL
                    trackButton.click();
                    const iframeLayer = await waitForElement('.layui-layer-iframe iframe', 5000);
                    trackUrl = iframeLayer.src;

                    // 关闭弹窗
                    const closeBtn = document.querySelector('.layui-layer-close');
                    if (closeBtn) closeBtn.click();
                }

                // 创建隐藏 iframe
                const hiddenIframe = document.createElement('iframe');
                hiddenIframe.style.display = 'none';
                hiddenIframe.src = trackUrl;
                document.body.appendChild(hiddenIframe);

            } catch (error) {
                displayNames(['获取流程跟踪URL失败']);
            }
        }

        function displayNames(names) {
            if (!Array.isArray(names) || names.length === 0) {
                names = ['未找到后续人员'];
            }
            const existingBox = document.getElementById('tm-next-handlers-display');
            if (existingBox) existingBox.remove();

            const displayBox = document.createElement('div');
            displayBox.id = 'tm-next-handlers-display';
            displayBox.style.cssText = 'position:fixed;top:10px;right:10px;background-color:#e8f4ff;border:1px solid #1890ff;border-radius:4px;padding:10px;z-index:999999;font-size:14px;box-shadow:0 2px 8px rgba(0,0,0,0.15);max-width:300px;word-wrap:break-word;font-family:monospace;';
            displayBox.innerHTML = `<strong style="color:#1890ff;">${TARGET_HANDLER_NAME}${ACTION_KEYWORD}后续:</strong>
<br>` +
                names.map(name => `<span style="display:inline-block;margin:3px 5px;background-color:#f0f0f0;padding:2px 5px;border-radius:3px;">${name}</span>`).join(' ');
            document.body.appendChild(displayBox);
        }

        // 接收 iframe 发来的结果
        window.addEventListener('message', (event) => {
            if (event.data && event.data.type === 'tm-next-handlers') {
                displayNames(event.data.names);
            }
        });

        window.addEventListener('load', openHiddenProcessTrack);
    }

    // ================= iframe 页面逻辑 =================
    if (location.pathname.includes('/mocha.component.processtrack.v5/process/processinfo')) {
        // 在流程跟踪页执行原有提取逻辑
        function extractNames() {
            try {
                const handlerSpans = document.querySelectorAll('span.handler');
                for (let span of handlerSpans) {
                    if (span.textContent.trim() === TARGET_HANDLER_NAME) {
                        const handlerRow = span.closest('tr');
                        if (!handlerRow) continue;

                        let submitLink = null;
                        const nextRow = handlerRow.nextElementSibling;
                        if (nextRow) {
                            const linksInNextRow = nextRow.querySelectorAll('a.submit-path-link');
                            for (const link of linksInNextRow) {
                                if (link.textContent.includes(ACTION_KEYWORD)) {
                                    submitLink = link; break;
                                }
                            }
                        }
                        if (!submitLink) {
                            const linksInRow = handlerRow.querySelectorAll('a.submit-path-link');
                            for (const link of linksInRow) {
                                if (link.textContent.includes(ACTION_KEYWORD)) {
                                    submitLink = link; break;
                                }
                            }
                        }
                        if (submitLink) {
                            const onMouseOverAttr = submitLink.getAttribute('onmouseover');
                            if (onMouseOverAttr && onMouseOverAttr.startsWith('showTip')) {
                                const matches = onMouseOverAttr.match(/showTip\(\s*this\s*,\s*['"]([^'"]*)['"]\s*,\s*['"]([^'"]*)['"]\s*\)/);
                                if (matches && matches.length >= 3) {
                                    const mc = matches[1], tiid = matches[2];
                                    return fetchNextHandlers(mc, tiid);
                                }
                            }
                        }
                    }
                }
                return [];
            } catch (e) {
                return [];
            }
        }

        function fetchNextHandlers(mc, tiid) {
            try {
                let basePath = '/mocha.component.processtrack.v5';
                if (typeof window.FRAMEWORK_BASE_PATH !== 'undefined') {
                    basePath = window.FRAMEWORK_BASE_PATH;
                }
                const xhr = new XMLHttpRequest();
                xhr.open('GET', `${basePath}/process/getNextHandlers?mc=${encodeURIComponent(mc)}&tiid=${encodeURIComponent(tiid)}`, false);
                xhr.withCredentials = true;
                xhr.send();
                if (xhr.status === 200) {
                    const data = JSON.parse(xhr.responseText);
                    if (data.result === true && data.data && Array.isArray(data.data)) {
                        return data.data.map(item => item.handler).filter(Boolean);
                    }
                }
            } catch (e) {}
            return [];
        }

        window.addEventListener('load', () => {
            const names = extractNames();
            // 发送给父页面
            if (window.parent && window.parent !== window) {
                window.parent.postMessage({ type: 'tm-next-handlers', names }, '*');
            }
        });
    }

})();