Greasy Fork

Greasy Fork is available in English.

一键填充手机号

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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 });
    });

})();