490336534qq.com / 测试环境快速登录

// ==UserScript==
// @name         测试环境快速登录
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  支持邮箱/userId/obsId登录,并可一键复制历史内容。最终始终用userId(数字)登录
// @author       tingbai
// @license      MIT
// @match       http://localhost/*
// @match       http://localhost:*/*
// @match       https://localhost/*
// @match       https://localhost:*/*
// @match       http://127.0.0.1/*
// @match       http://127.0.0.1:*/*
// @match       https://127.0.0.1/*
// @match       https://127.0.0.1:*/*
// @match        http://zhuzhan.feat.qunhequnhe.com/*
// @match        https://sit.kujiale.com/*
// @match        http://tobdev.feat.qunhequnhe.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';
    // 样式
    const style = document.createElement('style');
    style.innerHTML = `
        .email-login-container {
            position: fixed;
            top: 100px;
            right: 0;
            z-index: 9999;
            background: white;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px 0 0 5px;
            box-shadow: -2px 2px 5px rgba(0,0,0,0.1);
            transition: all 0.3s ease;
            user-select: none;
        }
        .email-login-container.collapsed {
            transform: translateX(100%);
            padding: 0;
            border: none;
            box-shadow: none;
        }
        .email-login-input {
            padding: 8px;
            width: 200px;
            margin-right: 5px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
        .email-login-btn {
            padding: 8px 12px;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            flex-shrink: 0;
        }
        .email-login-btn:hover {
            background: #45a049;
        }
        .email-login-message {
            margin-top: 5px;
            font-size: 12px;
            color: #666;
        }
        .toggle-container {
            position: absolute;
            top: 50%;
            left: -20px;
            width: 20px;
            height: 40px;
            background: white;
            border: 1px solid #ddd;
            border-right: none;
            border-radius: 5px 0 0 5px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            transform: translateY(-50%);
            box-shadow: -2px 2px 5px rgba(0,0,0,0.1);
        }
        .toggle-container:hover {
            background: #f0f0f0;
        }
        .toggle-arrow {
            width: 0;
            height: 0;
            border-top: 6px solid transparent;
            border-bottom: 6px solid transparent;
            border-right: 6px solid #666;
            transition: all 0.3s ease;
        }
        .collapsed .toggle-arrow {
            transform: rotate(180deg);
        }
        .content-wrapper {
            display: flex;
            flex-direction: column;
            min-width: 250px;
        }
        .history-section {
            margin-top: 10px;
            border-top: 1px solid #eee;
            padding-top: 10px;
        }
        .history-title {
            font-size: 12px;
            color: #999;
            margin-bottom: 5px;
        }
        .history-list {
            display: flex;
            flex-direction: column;
            gap: 3px;
        }
        .history-item {
            padding: 3px 5px;
            font-size: 12px;
            background: #f5f5f5;
            border-radius: 3px;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .history-item:hover {
            background: #e9e9e9;
        }
        .history-item .delete-btn {
            color: #999;
            margin-left: 8px;
            visibility: hidden;
            cursor: pointer;
        }
        .history-item .copy-btn {
            color: #199ed8;
            margin-left: 8px;
            font-size: 12px;
            border: none;
            background: transparent;
            cursor: pointer;
            visibility: hidden;
        }
        .history-item:hover .delete-btn,
        .history-item:hover .copy-btn {
            visibility: visible;
        }
        .history-item .copy-btn.copied {
            color: #4CAF50;
        }
        .input-row {
            display: flex;
            align-items: center;
        }
    `;
    document.head.appendChild(style);

    // 容器
    const container = document.createElement('div');
    container.className = 'email-login-container';
    container.innerHTML = `
        <div class="toggle-container" title="显示登录面板">
            <div class="toggle-arrow"></div>
        </div>
        <div class="content-wrapper">
            <div class="input-row">
                <input type="text" class="email-login-input" placeholder="输入邮箱/userId/obsId">
                <button class="email-login-btn">登录</button>
            </div>
            <div class="email-login-message"></div>
            <div class="history-section">
                <div class="history-title">最近登录</div>
                <div class="history-list"></div>
            </div>
        </div>
    `;
    document.body.appendChild(container);

    const input = container.querySelector('.email-login-input');
    const button = container.querySelector('.email-login-btn');
    const message = container.querySelector('.email-login-message');
    const toggleBtn = container.querySelector('.toggle-container');
    const historyList = container.querySelector('.history-list');

    const MAX_HISTORY = 5;
    let loginHistory = GM_getValue('loginHistory', []);

    function loginWithInput(val) {
        if (!val) {
            message.textContent = '请输入邮箱、userId或obsId';
            return;
        }
        val = val.trim();
        if (validateEmail(val)) {
            loginWithEmail(val);
        } else if (validatePureUserId(val)) {
            loginByUserId(val, "userId登录");
        } else if (validateObsId(val)) {
            loginWithObsId(val);
        } else {
            message.textContent = '请输入有效的邮箱、userId或obsId';
        }
    }

    function validateEmail(email) {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    }
    function validatePureUserId(str) {
        return /^\d{4,}$/.test(str);
    }
    function validateObsId(str) {
        return /^[A-Z0-9]{8,20}$/.test(str) && /[A-Z]/.test(str);
    }

    function loginWithEmail(email) {
        message.textContent = '正在查询用户ID...';
        GM_xmlhttpRequest({
            method: "POST",
            url: "https://colorful-dev.qunhequnhe.com/commercialization/api/arc-acs/check/business-accounts",
            headers: {
                "priority": "u=1, i",
                "Cookie": "pubinternalsso=xaYvBTv9Tji/vEBlYWjrxJizDv1rCcvUcsifAAqAJgw",
                "content-type": "application/json"
            },
            data: JSON.stringify({email: email}),
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.d && data.d[0] && data.d[0].userInfo && data.d[0].userInfo.userId) {
                        const userId = data.d[0].userInfo.userId;
                        message.textContent = "查得userId: " + userId + ",正在登录...";
                        addToHistory({type:'email', value:email, show:`${email} (uid:${userId})`});
                        loginByUserId(userId, "邮箱登录");
                    } else {
                        message.textContent = '未找到用户ID';
                    }
                } catch (e) {
                    message.textContent = '解析响应失败: ' + e.message;
                }
            },
            onerror: function(error) {
                message.textContent = '请求失败: ' + error.statusText;
            }
        });
    }

    function loginWithObsId(obsId) {
        message.textContent = "正在通过 obsId 查询 userId...";
        obsConvert(obsId, (userId, errMsg) => {
            if (userId && validatePureUserId(userId)) {
                addToHistory({type: 'obsId', value: obsId, show: `obsId:${obsId} (userId:${userId})`});
                loginByUserId(userId, "obsId登录");
            } else {
                message.textContent = 'obsId 转换为 userId 失败!' + (errMsg ? " 【" + errMsg + "】" : "");
            }
        }, 'obsToUserId');
    }

    function loginByUserId(userId, debugDesc) {
        if (!validatePureUserId(userId)) {
            message.textContent = 'userId必须为数字,实际是' + userId;
            return;
        }
        const currentDomain = window.location.protocol + "//" + window.location.host;
        const loginUrl = currentDomain + "/uic/trust/login?userId=" + userId;
        message.textContent = '跳转到登录页面...';
        window.location.href = loginUrl;
    }

    function obsConvert(id, cb, mode) {
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://coops.qunhequnhe.com/api/dbv/common/obsconvert?id=" + encodeURIComponent(id),
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data && data.c === "0" && data.d) {
                        cb(data.d);
                    } else {
                        cb(undefined, data && data.m ? data.m : "接口无效返回");
                    }
                } catch (e) {
                    cb(undefined, e.message);
                }
            },
            onerror: function(err) {
                cb(undefined, err.statusText || "网络错误");
            }
        });
    }

    // -------- 新增 复制功能 ---------
    function copyToClipboard(text) {
        // 极速方案
        if (navigator.clipboard) {
            navigator.clipboard.writeText(text).catch(()=>{});
        } else {
            const textarea = document.createElement('textarea');
            textarea.value = text;
            document.body.appendChild(textarea);
            textarea.select();
            try { document.execCommand('copy'); }catch(e){}
            document.body.removeChild(textarea);
        }
    }

    function renderHistory() {
        historyList.innerHTML = '';
        loginHistory.forEach((item, index) => {
            const dom = document.createElement('div');
            dom.className = 'history-item';
            dom.innerHTML = `
                <span>${item.show || item.value}</span>
                <button class="copy-btn" title="复制">复制</button>
                <span class="delete-btn" data-index="${index}">×</span>
            `;
            historyList.appendChild(dom);

            // 主体点击跳转
            dom.addEventListener('click', (e) => {
                if (
                    !e.target.classList.contains('delete-btn') &&
                    !e.target.classList.contains('copy-btn')
                ) {
                    loginWithInput(item.value);
                }
            });

            // 删除
            dom.querySelector('.delete-btn').addEventListener('click', (e) => {
                e.stopPropagation();
                loginHistory.splice(index, 1);
                GM_setValue('loginHistory', loginHistory);
                renderHistory();
            });

            // 复制
            const copyBtn = dom.querySelector('.copy-btn');
            copyBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                copyToClipboard(item.value);
                copyBtn.classList.add('copied');
                copyBtn.textContent = "已复制";
                setTimeout(() => {
                    copyBtn.classList.remove('copied');
                    copyBtn.textContent = "复制";
                }, 1200);
            });
        });
    }
    renderHistory();

    function addToHistory(newItem) {
        loginHistory = loginHistory.filter(item => item.value !== newItem.value);
        loginHistory.unshift(newItem);
        if (loginHistory.length > MAX_HISTORY) {
            loginHistory = loginHistory.slice(0, MAX_HISTORY);
        }
        GM_setValue('loginHistory', loginHistory);
        renderHistory();
    }

    toggleBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        container.classList.toggle('collapsed');
        toggleBtn.title = container.classList.contains('collapsed') ? '显示登录面板' : '隐藏登录面板';
    });

    button.addEventListener('click', () => {
        loginWithInput(input.value.trim());
    });
    input.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            loginWithInput(input.value.trim());
        }
    });

    container.classList.add('collapsed');
})();