一键填充手机号

支持在界面上需要输入手机号的地方一键填充手机号

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         一键填充手机号
// @namespace    http://tampermonkey.net/
// @version      4.0
// @description  支持在界面上需要输入手机号的地方一键填充手机号
// @author       Moodles
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY = "my_auto_phone_number";
    const isTopWindow = (window.self === window.top);

    let isClosedByUser = false;

    // ==============================
    // 1. 配置逻辑
    // ==============================
    function getOrAskPhoneNumber() {
        let phone = GM_getValue(STORAGE_KEY, "");
        if (!phone) {
            if (isTopWindow) {
                phone = prompt("请输入要自动填充的手机号:");
                if (phone && phone.trim()) {
                    GM_setValue(STORAGE_KEY, phone.trim());
                }
            } else {
                alert("请在网页主界面点击油猴菜单设置手机号");
                return null;
            }
        }
        return phone;
    }

    if (isTopWindow) {
        GM_registerMenuCommand("⚙️ 设置/修改手机号", () => {
            const current = GM_getValue(STORAGE_KEY, "");
            const input = prompt("请输入新的手机号:", current);
            if (input !== null) GM_setValue(STORAGE_KEY, input.trim());
        });
    }

    // ==============================
    // 2. 智能识别逻辑
    // ==============================
    function isCaptchaOrVerifyCode(input) {
        if (input.maxLength > 0 && input.maxLength < 11) return true;

        const attributes = (
            (input.name || "") +
            (input.id || "") +
            (input.className || "") +
            (input.placeholder || "")
        ).toLowerCase();

        const blackList = ["code", "verify", "captcha", "digits", "auth", "pass", "pwd", "验证码", "search", "搜索"];
        return blackList.some(keyword => attributes.includes(keyword));
    }

    function isPhoneInput(input) {
        if (input.offsetParent === null) return false;
        if (isCaptchaOrVerifyCode(input)) return false;

        const type = (input.type || "").toLowerCase();
        const attrStr = (
            (input.name || "") +
            (input.id || "") +
            (input.placeholder || "")
        ).toLowerCase();

        if (type === 'tel') return true;
        const whiteList = ['mobile', 'phone', 'user', 'tel', '手机', '账号', '登录'];
        if (whiteList.some(key => attrStr.includes(key))) return true;

        return false;
    }

    function findValidInput() {
        const inputs = document.querySelectorAll('input');
        for (let input of inputs) {
            if (isPhoneInput(input)) return true;
        }
        return false;
    }

    // ==============================
    // 3. 填充逻辑
    // ==============================
    function fillPhoneNumber() {
        const phone = getOrAskPhoneNumber();
        if (!phone) return;

        const inputs = document.querySelectorAll('input');
        let hasFilled = false;

        for (let input of inputs) {
            if (hasFilled) break;
            if (isPhoneInput(input)) {
                simulateInput(input, phone);
                hasFilled = true;
                input.style.backgroundColor = "#e8f0fe";
                input.style.transition = "background-color 0.5s";
                setTimeout(() => input.style.backgroundColor = "", 1000);
            }
        }

        if (!hasFilled) {
            const active = document.activeElement;
            if (active && active.tagName === "INPUT") {
                 simulateInput(active, phone);
            }
        }
    }

    function simulateInput(element, value) {
        element.focus();
        const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
        nativeInputValueSetter.call(element, value);
        ['input', 'change', 'blur'].forEach(type => {
            element.dispatchEvent(new Event(type, { bubbles: true }));
        });
    }

    // ==============================
    // 4. 界面 UI
    // ==============================
    function createButton() {
        if (isClosedByUser) return;

        if (document.getElementById('tm-auto-fill-container')) return;
        if (!findValidInput()) return;

        const container = document.createElement('div');
        container.id = 'tm-auto-fill-container';
        Object.assign(container.style, {
            position: "fixed", bottom: "20px", left: "20px", zIndex: "2147483647",
            width: "40px", height: "40px", cursor: "pointer", userSelect: "none",
            fontFamily: "sans-serif"
        });

        const mainBtn = document.createElement('div');
        Object.assign(mainBtn.style, {
            width: "100%", height: "100%", borderRadius: "50%",
            backgroundColor: "#3b82f6", color: "white",
            display: "flex", alignItems: "center", justifyContent: "center",
            fontSize: "20px", boxShadow: "0 4px 10px rgba(0,0,0,0.2)",
            transition: "transform 0.1s, opacity 0.2s"
        });
        mainBtn.innerText = "📱";
        mainBtn.title = "点击填充手机号";

        container.onmouseenter = () => mainBtn.style.transform = "scale(1.05)";
        container.onmouseleave = () => mainBtn.style.transform = "scale(1)";

        mainBtn.onclick = (e) => {
            e.stopPropagation();
            fillPhoneNumber();
        };

        const closeBtn = document.createElement('div');
        closeBtn.innerText = "×";
        closeBtn.title = "关闭此按钮";
        Object.assign(closeBtn.style, {
            position: "absolute", top: "-5px", right: "-5px",
            width: "16px", height: "16px", borderRadius: "50%",
            backgroundColor: "#ef4444", color: "white",
            fontSize: "12px", fontWeight: "bold", lineHeight: "14px", textAlign: "center",
            boxShadow: "0 2px 4px rgba(0,0,0,0.2)", display: "none"
        });

        container.onmouseenter = () => {
            mainBtn.style.transform = "scale(1.05)";
            closeBtn.style.display = "block";
        };
        container.onmouseleave = () => {
            mainBtn.style.transform = "scale(1)";
            closeBtn.style.display = "none";
        };

        closeBtn.onclick = (e) => {
            e.stopPropagation();
            isClosedByUser = true;
            container.remove();
        };

        container.appendChild(mainBtn);
        container.appendChild(closeBtn);
        document.body.appendChild(container);
    }

    const observer = new MutationObserver(() => {
        // Observer 触发时,先检查用户是不是之前关掉过
        if (!isClosedByUser && !document.getElementById('tm-auto-fill-container')) {
             createButton();
        }
    });

    window.addEventListener('load', () => {
        createButton();
        observer.observe(document.body, { childList: true, subtree: true });
    });

})();