吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6451|回复: 72
收起左侧

[其他原创] 【油猴脚本】智能自动填表助手 告别重复填表!!

    [复制链接]
MRBANK 发表于 2026-1-28 16:02
本帖最后由 MRBANK 于 2026-1-28 16:54 编辑

脚本介绍:
智能自动填表助手,专为解决重复填写表单的烦恼而设计。通过智能识别和保存表单数据,让您告别繁琐的重复输入,一键完成表单填充。
无论是登录账号、填写个人信息,还是提交各类申请表,这款脚本都能帮您轻松搞定。
核心功能
1. 智能表单捕获
  • 鼠标悬停识别:移动鼠标到表单上自动高亮显示
  • 一键保存:点击即可保存表单字段
  • 智能识别:自动识别输入框类型(文本、密码、邮箱等)
  • 自动命名:根据placeholder或name属性智能命名
2. 数据管理
  • 本地存储:所有数据安全保存在本地,保护隐私
  • 网站隔离:按域名分类存储,不同网站数据互不干扰
  • 实时编辑:可随时修改保存的填充内容
  • 灵活删除:支持删除单个字段或清空所有数据
3. 自动填充
  • 一键填充:瞬间完成所有已保存表单的填充
  • 视觉反馈:填充时高亮显示,确认填充成功
  • 事件触发:自动触发input和change事件,确保网站正确响应
  • 填充统计:显示成功填充的表单数量
4. 界面设计
  • 现代UI:紫色渐变主题,毛玻璃效果
  • 悬浮球设计:智能显示/隐藏,不干扰浏览
  • 动画效果:流畅的过渡动画,操作体验丝滑
  • 视觉提示:友好的通知提醒,操作状态一目了然

Snipaste_2026-01-28_15-54-26.jpg
// ==UserScript==
// @name         智能自动填表助手
// @namespace    http://tampermonkey.net/
// @version      1.1.1
// @description  鼠标悬停获取表单,自动保存并填充表单内容,现代化UI设计
// @author       MRBANK
// @match        *://*/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSI+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdHlsZT0ic3RvcC1jb2xvcjojNjY3ZWVhO3N0b3Atb3BhY2l0eToxIiAvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiM3NjRiYTI7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8cGF0aCBkPSJNOSAzVjRNMTIgM1Y0TTE1IDNWNE02IDdIMThDMTkuMTA0NiA3IDIwIDcuODk1NDMgMjAgOVYxOUMyMCAyMC4xMDQ2IDE5LjEwNDYgMjEgMTggMjFINkM0Ljg5NTQzIDIxIDQgMjAuMTA0NiA0IDE5VjlDNCA3Ljg5NTQzIDQuODk1NDMgNyA2IDdaIiBzdHJva2U9InVybCgjZ3JhZCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CiAgPHBhdGggZD0iTTggMTFIMTZNOCAxNUgxMyIgc3Ryb2tlPSJ1cmwoI2dyYWQpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4=
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // 添加样式
    GM_addStyle(`
        /* 主面板样式 */
        #autoFillPanel {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 420px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.3);
            z-index: 9999999;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            transition: all 0.3s ease;
            transform: translateX(440px);
            opacity: 0;
        }

        #autoFillPanel.show {
            transform: translateX(0);
            opacity: 1;
        }

        .panel-header {
            background: rgba(255,255,255,0.1);
            padding: 20px;
            border-radius: 20px 20px 0 0;
            backdrop-filter: blur(10px);
            border-bottom: 1px solid rgba(255,255,255,0.2);
        }

        .panel-title {
            color: white;
            font-size: 20px;
            font-weight: 600;
            margin: 0;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .close-btn {
            background: rgba(255,255,255,0.2);
            border: none;
            color: white;
            width: 30px;
            height: 30px;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
        }

        .close-btn:hover {
            background: rgba(255,255,255,0.3);
            transform: rotate(90deg);
        }

        .panel-content {
            background: white;
            padding: 20px;
            padding-bottom: 10px;
            border-radius: 0 0 20px 20px;
            max-height: 500px;
            overflow-y: auto;
            position: relative;
        }

        /* 悬浮按钮 */
        #floatingBtn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            width: 60px;
            height: 60px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 50%;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
            cursor: pointer;
            z-index: 9999998;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 24px;
            transition: all 0.3s ease;
            animation: fadeIn 0.5s ease;
        }

        #floatingBtn:hover {
            transform: scale(1.1);
            box-shadow: 0 15px 40px rgba(0,0,0,0.4);
        }

        #floatingBtn.hidden {
            display: none;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: scale(0.5);
            }
            to {
                opacity: 1;
                transform: scale(1);
            }
        }

        /* 表单项样式 */
        .form-item {
            background: #f7f7fc;
            padding: 15px;
            margin-bottom: 15px;
            border-radius: 12px;
            border: 2px solid transparent;
            transition: all 0.3s ease;
            position: relative;
        }

        .form-item:hover {
            border-color: #667eea;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102,126,234,0.1);
        }

        .form-item-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .form-item-title {
            font-weight: 600;
            color: #2d3748;
            font-size: 14px;
        }

        .form-item-type {
            font-size: 12px;
            color: #667eea;
            background: rgba(102,126,234,0.1);
            padding: 2px 8px;
            border-radius: 12px;
        }

        .form-item input, .form-item textarea {
            width: 100%;
            padding: 10px 15px;
            border: 1px solid #e2e8f0;
            border-radius: 8px;
            font-size: 14px;
            transition: all 0.3s ease;
            box-sizing: border-box;
        }

        .form-item input:focus, .form-item textarea:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102,126,234,0.1);
        }

        .delete-btn {
            position: absolute;
            top: 10px;
            right: 10px;
            background: #fc8181;
            color: white;
            border: none;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            opacity: 0;
            transition: all 0.3s ease;
        }

        .form-item:hover .delete-btn {
            opacity: 1;
        }

        .delete-btn:hover {
            background: #f56565;
            transform: scale(1.1);
        }

        /* 按钮样式 */
        .action-buttons {
            display: flex;
            gap: 8px;
            margin-top: 20px;
            position: sticky;
            bottom: 0;
            background: white;
            padding: 15px 0 5px 0;
            margin-left: -20px;
            margin-right: -20px;
            padding-left: 20px;
            padding-right: 20px;
            border-top: 1px solid #e2e8f0;
        }

        .btn {
            padding: 12px 16px;
            border: none;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            flex: 1;
            white-space: nowrap;
            min-width: 0;
            text-align: center;
        }

        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102,126,234,0.3);
        }

        .btn-secondary {
            background: #f7f7fc;
            color: #2d3748;
        }

        .btn-secondary:hover {
            background: #e2e8f0;
        }

        .btn-danger {
            background: #fc8181;
            color: white;
        }

        .btn-danger:hover {
            background: #f56565;
        }

        /* 提示样式 */
        .empty-state {
            text-align: center;
            padding: 40px;
            color: #a0aec0;
        }

        .empty-state-icon {
            font-size: 48px;
            margin-bottom: 16px;
        }

        /* 高亮样式 */
        .highlight-element {
            outline: 3px solid #667eea !important;
            outline-offset: 2px !important;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0% { outline-color: #667eea; }
            50% { outline-color: #764ba2; }
            100% { outline-color: #667eea; }
        }

        /* 提示框样式 */
        .tooltip {
            position: absolute;
            background: #2d3748;
            color: white;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 12px;
            pointer-events: none;
            z-index: 9999999;
            white-space: nowrap;
        }

        .tooltip::after {
            content: '';
            position: absolute;
            bottom: -4px;
            left: 50%;
            transform: translateX(-50%);
            width: 0;
            height: 0;
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-top: 4px solid #2d3748;
        }

        /* 通知样式 */
        .notification {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: #48bb78;
            color: white;
            padding: 12px 24px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 10000000;
            animation: slideDown 0.3s ease;
        }

        @keyframes slideDown {
            from {
                transform: translateX(-50%) translateY(-100%);
            }
            to {
                transform: translateX(-50%) translateY(0);
            }
        }
    `);

    // 数据存储
    let captureMode = false;
    let formData = GM_getValue('formData', {});
    let floatingBtn = null;
    let panel = null;

    // 检查当前网站是否有数据
    function hasDataForCurrentSite() {
        const currentHost = window.location.hostname;
        return formData[currentHost] && Object.keys(formData[currentHost]).length > 0;
    }

    // 初始化UI
    function initUI() {
        // 创建主面板
        panel = document.createElement('div');
        panel.id = 'autoFillPanel';
        panel.innerHTML = `
            <div class="panel-header">
                <h2 class="panel-title">
                    <span>📝 智能填表助手</span>
                    <button class="close-btn">✕</button>
                </h2>
            </div>
            <div class="panel-content">
                <div id="formsList"></div>
                <div class="action-buttons">
                    <button class="btn btn-primary" id="startCapture">开始捕获</button>
                    <button class="btn btn-secondary" id="autoFill">自动填充</button>
                    <button class="btn btn-danger" id="clearAll">清空数据</button>
                </div>
            </div>
        `;
        document.body.appendChild(panel);

        // 创建悬浮按钮
        floatingBtn = document.createElement('div');
        floatingBtn.id = 'floatingBtn';
        floatingBtn.innerHTML = '📋';
        floatingBtn.className = hasDataForCurrentSite() ? '' : 'hidden';
        document.body.appendChild(floatingBtn);

        // 绑定事件
        bindEvents();
    }

    // 绑定事件
    function bindEvents() {
        // 悬浮按钮点击
        floatingBtn.addEventListener('click', () => {
            panel.classList.toggle('show');
            updateFormsList();
        });

        // 关闭按钮
        panel.querySelector('.close-btn').addEventListener('click', () => {
            panel.classList.remove('show');
        });

        // 开始捕获
        document.getElementById('startCapture').addEventListener('click', () => {
            captureMode = !captureMode;
            document.getElementById('startCapture').textContent = captureMode ? '停止捕获' : '开始捕获';

            if (captureMode) {
                panel.classList.remove('show');
                startCapture();
            } else {
                stopCapture();
            }
        });

        // 自动填充
        document.getElementById('autoFill').addEventListener('click', autoFill);

        // 清空数据
        document.getElementById('clearAll').addEventListener('click', clearAllData);
    }

    // 更新表单列表
    function updateFormsList() {
        const formsList = document.getElementById('formsList');
        const currentHost = window.location.hostname;
        const currentData = formData[currentHost] || {};

        if (Object.keys(currentData).length === 0) {
            formsList.innerHTML = `
                <div class="empty-state">
                    <div class="empty-state-icon">📭</div>
                    当前页面暂无保存的表单数据
                    <p style="font-size: 12px;">点击"开始捕获"来添加表单</p>
                </div>
            `;
        } else {
            formsList.innerHTML = Object.entries(currentData).map(([selector, data]) => `
                <div class="form-item" data-selector="${selector}">
                    <button class="delete-btn" title="删除此项">×</button>
                    <div class="form-item-header">
                        <span class="form-item-title">${data.label || selector}</span>
                        <span class="form-item-type">${data.type || 'text'}</span>
                    </div>
                    ${data.type === 'textarea' ?
                        `<textarea placeholder="输入内容...">${data.value || ''}</textarea>` :
                        `<input type="${data.type || 'text'}" placeholder="输入内容..." value="${data.value || ''}">`
                    }
                </div>
            `).join('');

            // 添加输入事件监听
            formsList.querySelectorAll('input, textarea').forEach(input => {
                input.addEventListener('input', (e) => {
                    const selector = e.target.closest('.form-item').dataset.selector;
                    if (!formData[currentHost]) formData[currentHost] = {};
                    formData[currentHost][selector].value = e.target.value;
                    GM_setValue('formData', formData);
                });
            });

            // 添加删除按钮事件
            formsList.querySelectorAll('.delete-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const selector = e.target.closest('.form-item').dataset.selector;
                    delete formData[currentHost][selector];

                    // 如果没有数据了,隐藏悬浮球
                    if (Object.keys(formData[currentHost]).length === 0) {
                        delete formData[currentHost];
                        floatingBtn.classList.add('hidden');
                    }

                    GM_setValue('formData', formData);
                    updateFormsList();
                });
            });
        }
    }

    // 创建提示框
    const tooltip = document.createElement('div');
    tooltip.className = 'tooltip';
    tooltip.style.display = 'none';
    document.body.appendChild(tooltip);

    // 显示通知
    function showNotification(message, duration = 3000) {
        const notification = document.createElement('div');
        notification.className = 'notification';
        notification.textContent = message;
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.remove();
        }, duration);
    }

    // 开始捕获
    function startCapture() {
        document.addEventListener('mouseover', handleMouseOver);
        document.addEventListener('click', handleClick, true);
        showNotification('捕获模式已开启,点击表单元素进行保存');
    }

    // 停止捕获
    function stopCapture() {
        document.removeEventListener('mouseover', handleMouseOver);
        document.removeEventListener('click', handleClick, true);
        document.querySelectorAll('.highlight-element').forEach(el => {
            el.classList.remove('highlight-element');
        });
        tooltip.style.display = 'none';
    }

    // 鼠标悬停处理
    function handleMouseOver(e) {
        if (!captureMode) return;

        const target = e.target;
        if (target.matches('input, textarea, select') && !target.closest('#autoFillPanel')) {
            // 移除之前的高亮
            document.querySelectorAll('.highlight-element').forEach(el => {
                el.classList.remove('highlight-element');
            });

            // 添加高亮
            target.classList.add('highlight-element');

            // 显示提示
            const rect = target.getBoundingClientRect();
            tooltip.textContent = '点击保存此表单';
            tooltip.style.display = 'block';
            tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + 'px';
            tooltip.style.top = rect.top - tooltip.offsetHeight - 8 + 'px';
        }
    }

    // 点击处理
    function handleClick(e) {
        if (!captureMode) return;

        const target = e.target;
        if (target.matches('input, textarea, select') && !target.closest('#autoFillPanel')) {
            e.preventDefault();
            e.stopPropagation();

            const selector = getUniqueSelector(target);
            const currentHost = window.location.hostname;

            if (!formData[currentHost]) formData[currentHost] = {};

            formData[currentHost][selector] = {
                type: target.type || 'text',
                label: target.placeholder || target.name || selector,
                value: target.value || ''
            };

            GM_setValue('formData', formData);

            // 显示悬浮球
            floatingBtn.classList.remove('hidden');

            // 停止捕获
            captureMode = false;
            stopCapture();
            document.getElementById('startCapture').textContent = '开始捕获';

            // 显示通知
            showNotification('✅ 表单已成功保存!');

            // 显示面板
            panel.classList.add('show');
            updateFormsList();
        }
    }

    // 获取唯一选择器
    function getUniqueSelector(element) {
        if (element.id) return `#${element.id}`;
        if (element.name) return `[name="${element.name}"]`;

        let selector = element.tagName.toLowerCase();
        if (element.className) {
            selector += `.${element.className.split(' ').filter(c => c).join('.')}`;
        }

        // 如果还不够唯一,添加索引
        const parent = element.parentElement;
        if (parent) {
            const siblings = parent.querySelectorAll(selector);
            if (siblings.length > 1) {
                const index = Array.from(siblings).indexOf(element);
                selector += `:nth-of-type(${index + 1})`;
            }
        }

        return selector;
    }

    // 自动填充
    function autoFill() {
        const currentHost = window.location.hostname;
        const currentData = formData[currentHost] || {};
        let filledCount = 0;

        Object.entries(currentData).forEach(([selector, data]) => {
            try {
                const element = document.querySelector(selector);
                if (element && data.value) {
                    element.value = data.value;
                    element.dispatchEvent(new Event('input', { bubbles: true }));
                    element.dispatchEvent(new Event('change', { bubbles: true }));

                    // 高亮填充的元素
                    element.classList.add('highlight-element');
                    setTimeout(() => {
                        element.classList.remove('highlight-element');
                    }, 2000);

                    filledCount++;
                }
            } catch (e) {
                console.error('填充失败:', selector, e);
            }
        });

        panel.classList.remove('show');
        showNotification(`✅ 成功填充 ${filledCount} 个表单项`);
    }

    // 清空数据
    function clearAllData() {
        if (confirm('确定要清空当前网站的所有表单数据吗?')) {
            const currentHost = window.location.hostname;
            delete formData[currentHost];
            GM_setValue('formData', formData);
            updateFormsList();
            floatingBtn.classList.add('hidden');
            showNotification('已清空当前网站的表单数据');
        }
    }

    // 注册菜单命令
    GM_registerMenuCommand('📝 打开智能填表助手', () => {
        panel.classList.add('show');
        updateFormsList();
    });

    GM_registerMenuCommand('➕ 开始捕获表单', () => {
        if (!captureMode) {
            captureMode = true;
            document.getElementById('startCapture').textContent = '停止捕获';
            panel.classList.remove('show');
            startCapture();
        }
    });

    // 初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initUI);
    } else {
        initUI();
    }
})();

免费评分

参与人数 23吾爱币 +30 热心值 +21 收起 理由
黄金体验 + 1 + 1 谢谢@Thanks!
DUJ + 1 用心讨论,共获提升!
莫奇 + 1 + 1 谢谢@Thanks!
lzm456lzm + 1 + 1 我很赞同!
小怪兽出现 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
抱歉、 + 1 用心讨论,共获提升!
Natu + 1 + 1 谢谢@Thanks!
aa20221101 + 1 + 1 热心回复!
wapj721 + 1 + 1 热心回复!
Ericlkp + 1 + 1 热心回复!
2020520 + 1 + 1 用心讨论,共获提升!
wuming4 + 1 + 1 谢谢@Thanks!
yxzzz666 + 1 + 1 我很赞同!
YYL7535 + 1 + 1 谢谢@Thanks!
yanglinman + 1 谢谢@Thanks!
RobinMaas + 1 + 1 用心讨论,共获提升!
1588 + 1 + 1 谢谢@Thanks!
sky木木 + 1 + 1 我很赞同!
qiuku + 1 + 1 用心讨论,共获提升!
wkdxz + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hdjkksj3366 + 1 + 1 谢谢@Thanks!
天天上天庭 + 1 + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| MRBANK 发表于 2026-1-28 16:10
雾都孤尔 发表于 2026-1-28 16:09
经常申请账号或登录什么的用这个省事,用户信息是存在浏览器缓存里的吗?

对,本地存储
knian 发表于 2026-1-29 15:50
我用AI写的一个,自动登录用的

[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// [url=home.php?mod=space&uid=170990]@name[/url]         网站登录配置管理工具
// [url=home.php?mod=space&uid=467642]@namespace[/url]    http://tampermonkey.net/
// [url=home.php?mod=space&uid=1248337]@version[/url]      3.1
// @description  紧凑模式的多网站登录配置管理工具,支持搜索、导入导出、选择器验证、先手点击
// [url=home.php?mod=space&uid=686208]@AuThor[/url]       DeepSeek
// [url=home.php?mod=space&uid=195849]@match[/url]        *://*/*
// [url=home.php?mod=space&uid=609072]@grant[/url]        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @grant        GM_listValues
// @grant        GM_setClipboard
// @run-at       document-idle
// ==/UserScript==

(function () {
    'use strict';
    // ===================== 执行保护机制 =====================
    // 1. 检查是否在iframe中
    if (window.top !== window.self) {
        console.log('登录配置管理工具:在iframe中,跳过执行');
        return;
    }

    // 2. 检查是否已经执行过(防止重复执行)
    if (window.loginManagerInitialized) {
        console.log('登录配置管理工具:已经初始化,跳过重复执行');
        return;
    }

    // 3. 检查是否在特殊页面中(如浏览器设置页面)
    if (window.location.protocol === 'chrome:' ||
        window.location.protocol === 'chrome-extension:' ||
        window.location.protocol === 'moz-extension:' ||
        window.location.protocol === 'edge:' ||
        window.location.protocol === 'about:' ||
        window.location.protocol === 'data:' ||
        window.location.protocol === 'file:') {
        console.log('登录配置管理工具:在特殊页面中,跳过执行');
        return;
    }

    // 4. 标记已初始化
    window.loginManagerInitialized = true;

    console.log('&#8505;&#65039; 登录配置管理工具:开始初始化');

    // ===================== 常量定义 =====================
    const CONSTANTS = {
        // 配置相关
        CONFIG_PREFIX: 'loginConfig_',
        DEFAULT_SELECTORS: {
            username: "input[placeholder*='用户']",
            password: "input[type='password']",
            loginButton: "button[type='submit']",
            preClickButtons: [] // 先手点击按钮选择器数组
        },

        // 加密相关
        ENCRYPTION: {
            // 使用固定的盐值,增加安全性(实际应用中可以考虑用户特定的盐值)
            SALT: 'login_manager_2024',
            // 主密码(建议修改为你自己的强密码,例如:'MyStr0ng!P@ssw0rd#2024')
            MASTER_PASSWORD: 'MyStr0ng!P@ssw0rd#2024',
            // 加密算法标识
            ALGORITHM: 'AES-GCM',
            // 密钥派生函数参数
            KEY_DERIVATION: {
                iterations: 100000,
                keyLength: 256
            },
            // 加密标识前缀
            ENCRYPTED_PREFIX: 'ENCRYPTED:'
        },

        // UI相关
        PANEL_DIMENSIONS: {
            width: '480px',
            height: {
                collapsed: '428px',
                expanded: '100%'
            }
        },

        // 动画相关
        ANIMATION: {
            duration: '0.2s',
            debounceDelay: 400,
            highlightDuration: 2000,
            autoLoginDelay: 800,
            pageLoadDelay: 1500,
            preClickDelay: 500 // 先手点击后的等待时间
        },

        // 透明度相关
        OPACITY: {
            dragging: '0.7',
            normal: '1'
        },

        // 弹窗检测相关
        POPUP: {
            minZIndex: 1000,
            selectors: [
                '[role="dialog"]',
                '[role="alertdialog"]',
                '.modal',
                '.popup',
                '.dialog',
                '.overlay',
                '.lightbox',
                '.tooltip',
                '.dropdown',
                '.menu',
                '.notification',
                '.alert',
                '.toast',
                '#login-manager-compact',
                '#import-dialog'
            ],
            keywords: [
                'modal', 'popup', 'dialog', 'overlay', 'lightbox', 'tooltip',
                'dropdown', 'menu', 'notification', 'alert', 'toast', 'float',
                'fixed', 'absolute', 'z-index'
            ]
        },

        // 选择器生成相关
        SELECTOR: {
            userKeywords: ['user', 'name', 'account', 'login'],
            loginKeywords: ['登录', 'login', 'sign in'],
            genericClasses: ['btn', 'button', 'input', 'form-control', 'form-group']
        },

        // 消息相关
        MESSAGES: {
            errors: {
                urlRequired: '网站地址不能为空',
                nicknameRequired: '昵称不能为空',
                configRequired: '请先选择一个配置',
                urlInvalid: '网址格式不正确',
                importRequired: '请输入要导入的JSON',
                importFormat: '格式应为数组',
                importFailed: '导入失败: ',
                exportFailed: '导出失败',
                readFailed: '读取配置失败',
                saveFailed: '保存配置失败',
                deleteFailed: '删除配置失败',
                popupElement: '不能选择弹窗元素,请选择页面主体内容',
                passwordDecryptFailed: '密码解密失败,可能是主密码不同',
                newPasswordRequired: '请输入新密码',
                cryptoUnsupported: '当前浏览器环境不支持加密功能,请更换浏览器或禁用加密选项'
            },
            success: {
                configSaved: '配置已保存',
                configDeleted: '配置已删除',
                exportSuccess: '所有配置已复制到剪贴板!',
                importSuccess: '成功导入 ',
                configs: ' 条配置',
                passwordHandled: '密码处理完成'
            },
            confirm: {
                deleteConfig: '确定要删除此配置吗?此操作不可恢复!'
            },
            info: {
                encryptedPasswordDetected: '检测到加密密码,请选择处理方式',
                manualPasswordRequired: '请手动输入密码',
                exportPasswordOption: '请选择导出时的密码处理方式'
            }
        }
    };

    // ===================== 样式 =====================
    GM_addStyle(`
        #login-manager-compact { position: fixed; top: 20px; left: 20px; width: 480px; background: #fff; border-radius: 6px; box-shadow: 0 4px 20px rgba(0,0,0,0.25); z-index: 9999999; font-family: 'Segoe UI', Arial, sans-serif; overflow: hidden; border: 1px solid #ddd; font-size: 13px; display: none; }
        #login-manager-compact.dragging { opacity: 0.8; box-shadow: 0 8px 25px rgba(0,0,0,0.6); }
        #login-manager-compact .header { background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; padding: 10px 12px; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; }
        #login-manager-compact .title { font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
        #login-manager-compact .close-btn { background: none; border: none; color: white; font-size: 18px; cursor: pointer; padding: 3px 8px; line-height: 1; }
        #login-manager-compact .close-btn:hover { transform: scale(1.1); }
        #login-manager-compact .body { display: flex; height: 100%; min-height: 0; }
        #login-manager-compact .config-list-area { width: 180px; border-right: 1px solid #eee; background: #f9fafc; display: flex; flex-direction: column; height: 100%;max-height: 737px; min-height: 0; }
        #login-manager-compact .list-actions { display: flex; gap: 8px; padding: 10px 10px 12px 10px; border-bottom: 1px solid #eee; background: #f5f5f5; }
        #login-manager-compact .search-box { padding: 10px; border-bottom: 1px solid #eee; }
        #login-manager-compact .search-input { width: 100%; min-width: 0; box-sizing: border-box; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; }
        #login-manager-compact .config-list { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #b3b3b3 #f1f1f1; }
        #login-manager-compact .config-list::-webkit-scrollbar { width: 6px; background: #f1f1f1; }
        #login-manager-compact .config-list::-webkit-scrollbar-thumb { background: #b3b3b3; border-radius: 4px; }
        #login-manager-compact .config-list::-webkit-scrollbar-thumb:hover { background: #888; }
        #login-manager-compact .config-item { padding: 10px 12px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: all 0.2s; }
        #login-manager-compact .config-item.active { background: #e1e8ff; border-left: 3px solid #4d7cfe; }
        #login-manager-compact .config-item.current-page { background: #f0f8ff; border-left: 3px solid #28a745; }
        #login-manager-compact .config-item.current-page .nickname { color: #28a745; }
        #login-manager-compact .config-item .nickname { font-weight: 600; font-size: 13px; }
        #login-manager-compact .config-item .url { font-size: 11px; color: #888; }
        #login-manager-compact .list-btn { flex: 1; padding: 8px 0; border: none; border-radius: 6px; background: #4d7cfe; color: white; font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s; box-shadow: 0 1px 2px rgba(0,0,0,0.04); margin: 0; height: 36px; display: flex; align-items: center; justify-content: center; gap: 6px; }
        #login-manager-compact .list-btn.export { background: linear-gradient(to right, #00c9a7, #00b09b); }
        #login-manager-compact .list-btn.import { background: linear-gradient(to right, #e74c3c, #c0392b); }
        #login-manager-compact .list-btn:hover { opacity: 0.92; box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
        #login-manager-compact .form-area { flex: 1; padding: 18px 18px 0 18px; display: flex; flex-direction: column; }
        #login-manager-compact .form-group { margin-bottom: 12px; }
        #login-manager-compact .form-group label { display: block; margin-bottom: 5px; font-weight: 500; color: #555; font-size: 12px; }
        #login-manager-compact .compact-input { width: 100%; padding: 8px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; box-sizing: border-box; }
        #login-manager-compact .compact-input:focus { border-color: #3498db; outline: none; box-shadow: 0 0 0 2px rgba(52,152,219,0.2); }
        #login-manager-compact .input-group { display: flex; position: relative; }
        #login-manager-compact .input-group input { flex: 1; border-radius: 4px 0 0 4px !important; border-right: none !important; }
        #login-manager-compact .input-group .icon-btn { background: #f0f0f0; border: 1px solid #ddd; border-left: none; border-radius: 0 4px 4px 0; padding: 0 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; min-width: 36px; font-size: 14px; }
        #login-manager-compact .input-group .icon-btn:hover { background: #e0e0e0; }
        #login-manager-compact .selector-status { min-width: 36px; font-size: 14px; }
        #login-manager-compact .checkbox-group { display: flex; align-items: center; margin-top: 8px; }
        #login-manager-compact .checkbox-group input { margin-right: 6px; }
        #login-manager-compact .form-actions { display: flex; gap: 8px; padding: 12px 0 18px 0; }
        #login-manager-compact .form-btn { flex: 1; padding: 8px 10px; border: none; border-radius: 4px; font-weight: 600; cursor: pointer; font-size: 13px; }
        #login-manager-compact .form-btn.save { background: linear-gradient(to right, #3498db, #1d6fa5); color: white; }
        #login-manager-compact .form-btn.delete { background: linear-gradient(to right, #e74c3c, #c0392b); color: white; }
        #login-manager-compact .form-btn.new { background: linear-gradient(to right, #00b09b, #96c93d); color: white; }
        #login-manager-compact .form-group.selector-toggle-row { background: #f0f0f0; padding: 8px 12px; border-bottom: 1px solid #eee; display: flex; align-items: center; justify-content: space-between; cursor: pointer; }
        #login-manager-compact .form-group.selector-toggle-row label { font-weight: 600; color: #333; }
        #login-manager-compact #selectors-section { padding: 0 12px 12px 12px; border-bottom: 1px solid #eee; }
        #login-manager-compact #selectors-section .form-group { margin-bottom: 10px; }
        #login-manager-compact #selectors-section .form-group label { font-weight: 500; color: #555; }
        #import-textarea { width: 100%; height: 180px; padding: 8px; border: 2px solid #3498db; border-radius: 6px; font-family: monospace; margin-bottom: 12px; resize: vertical; font-size: 13px; box-sizing: border-box; outline: none; transition: border-color 0.2s; }
        #import-textarea:focus { border-color: #2575fc; box-shadow: 0 0 0 2px rgba(52,152,219,0.15); }
        #login-manager-compact input[type="checkbox"] { width: 16px !important; height: 16px !important; margin: 0 6px 0 0 !important; vertical-align: middle !important; accent-color: #4d7cfe !important; box-shadow: none !important; border: 1px solid #bbb !important; background: #fff !important; appearance: checkbox !important; -webkit-appearance: checkbox !important; outline: none !important; display: inline-block !important; position: relative; }
        #login-manager-compact label { font-size: 13px !important; color: #333 !important; font-weight: 400 !important; margin: 0 4px 0 0 !important; vertical-align: middle !important; user-select: none; }
        #login-manager-compact button, #login-manager-compact input, #login-manager-compact .form-group { box-sizing: border-box !important; }
        #login-manager-compact .checkbox-group { display: flex !important; align-items: center !important; gap: 8px !important; margin-bottom: 0 !important; }
        #login-manager-compact .checkbox-group label { margin: 0 12px 0 0 !important; }
        #import-dialog { z-index: 99999999 !important; }
        @media (max-width: 600px) { #login-manager-compact { width: 98%; top: 20px; font-size: 12px; } }
        @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
        @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
    `);

    // ===================== 主面板HTML =====================
    const managerHTML = `
        <div id="login-manager-compact">
        <div class="header" id="panel-header">
            <button id="toggle-list-btn" style="background:none;border:none;color:white;font-size:16px;cursor:pointer;padding:0 8px;margin-right:8px;">&#128203;</button>
            <div class="title"><span>&#9881;</span> 登录配置管理</div>
            <button class="close-btn" id="close-panel">×</button>
            </div>
        <div class="body">
            <div class="config-list-area" id="config-list-area">
            <div class="list-actions">
                <button class="list-btn export" id="export-all-btn">全部导出</button>
                <button class="list-btn import" id="import-all-btn">导入</button>
            </div>
            <div class="search-box">
                <input type="text" class="search-input" id="config-search" placeholder="搜索昵称或URL...">
            </div>
            <div class="config-list" id="config-list"></div>
            </div>
            <div class="form-area">
            <div class="form-group">
                <label>网站地址</label>
                <div style="display:flex;align-items:center;gap:8px;">
                <input type="text" id="edit-url" class="compact-input" style="flex:1;">
                <button id="goto-url-btn" style="padding:6px 12px;border:none;border-radius:4px;background:#4d7cfe;color:white;font-size:13px;cursor:pointer;">跳转</button>
            </div>
            </div>
            <div class="form-group">
                <label>昵称</label>
                <input type="text" id="edit-nickname" class="compact-input">
                    </div>
            <div class="form-group">
                <label>用户名</label>
                <input type="text" id="edit-username" class="compact-input">
                    </div>
            <div class="form-group">
                <label>密码</label>
                    <div class="input-group">
                <input type="password" id="edit-password" class="compact-input">
                <div class="icon-btn" id="toggle-edit-password">&#128065;</div>
                    </div>
                    </div>
            <div class="checkbox-group" style="gap:18px;">
                    <input type="checkbox" id="edit-autoLogin">
                <label for="edit-autoLogin">自动登录</label>
                <input type="checkbox" id="edit-autoFill" style="margin-left:18px;">
                <label for="edit-autoFill">自动填充</label>
                    </div>
            <br/>
            <div class="form-group selector-toggle-row" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:2px;">
                <label style="font-weight:600;">选择器</label>
                <button id="toggle-selectors-btn" style="background:none;border:none;font-size:15px;cursor:pointer;outline:none;">▼</button>
                </div>
            <div id="selectors-section">
                <div class="form-group">
                <label>用户名选择器</label>
                <div class="input-group">
                    <input type="text" id="edit-username-selector" class="compact-input">
                    <div class="icon-btn selector-status" id="check-username-selector">&#128260;</div>
                    <div class="icon-btn" id="pick-username-selector" title="选择元素">&#127919;</div>
                    </div>
                    </div>
                <div class="form-group">
                <label>密码选择器</label>
                <div class="input-group">
                    <input type="text" id="edit-password-selector" class="compact-input">
                    <div class="icon-btn selector-status" id="check-password-selector">&#128260;</div>
                    <div class="icon-btn" id="pick-password-selector" title="选择元素">&#127919;</div>
                    </div>
                    </div>
                <div class="form-group">
                <label>登录按钮选择器</label>
                <div class="input-group">
                    <input type="text" id="edit-login-button-selector" class="compact-input">
                    <div class="icon-btn selector-status" id="check-login-button-selector">&#128260;</div>
                    <div class="icon-btn" id="pick-login-button-selector" title="选择元素">&#127919;</div>
                    </div>
                    </div>
                <div class="form-group">
                <label>先手点击按钮选择器</label>
                <div style="margin-bottom:8px;font-size:12px;color:#666;">
                    用于在登录前先点击某些按钮(如切换到账号密码登录模式)
                </div>
                <div class="input-group">
                    <input type="text" id="edit-pre-click-selector" class="compact-input" placeholder="多个选择器用逗号分隔">
                    <div class="icon-btn selector-status" id="check-pre-click-selector">&#128260;</div>
                    <div class="icon-btn" id="pick-pre-click-selector" title="选择元素">&#127919;</div>
                    </div>
                    </div>
                </div>
            <div class="form-actions">
                <button class="form-btn save" id="save-config-btn">保存</button>
                <button class="form-btn delete" id="delete-config-btn">删除</button>
                <button class="form-btn new" id="new-config-btn">新建</button>
            </div>
            </div>
        </div>
        </div>
    `;
    document.body.insertAdjacentHTML('beforeend', managerHTML);
    const manager = document.getElementById('login-manager-compact');

    // ===================== 模块化骨架 =====================
    // 数据管理模块
    const DataManager = {
        CONFIG_PREFIX: CONSTANTS.CONFIG_PREFIX,
        // 配置版本管理
        CONFIG_VERSION: '1.0',
        /**
         * 升级配置到最新版本
         * @param {Object} config 配置对象
         * @returns {Object} 升级后的配置对象
         */
        upgradeConfig(config) {
            // 检查配置是否有效
            if (!config || typeof config !== 'object') {
                console.warn('upgradeConfig: 配置对象无效', config);
                return null;
            }

            if (!config.version) {
                // 从旧版本升级到1.0
                config.version = this.CONFIG_VERSION;
                config.createdAt = new Date().toISOString();
                config.updatedAt = new Date().toISOString();
            }
            return config;
        },
        /**
         * 获取配置模板
         * @param {string} templateName 模板名称
         * @returns {Object} 配置模板
         */
        getConfigTemplate(templateName) {
            const templates = {
                'default': {
                    currentUrl: '',
                    nickname: '新配置',
                    username: '',
                    password: '',
                    autoLogin: false,
                    autoFill: false,
                    selectors: {
                        ...CONSTANTS.DEFAULT_SELECTORS,
                        preClickButtons: [] // 先手点击按钮选择器数组
                    },
                    version: this.CONFIG_VERSION,
                    createdAt: new Date().toISOString(),
                    updatedAt: new Date().toISOString()
                }
            };
            return templates[templateName] || templates['default'];
        },
        /**
         * 验证配置数据
         * @param {Object} config 配置对象
         * @returns {Object} 验证结果 {valid: boolean, errors: Array}
         */
        validateConfig(config) {
            const errors = [];

            if (!config.currentUrl || !config.currentUrl.trim()) {
                errors.push('网站地址不能为空');
            } else {
                try {
                    new URL(config.currentUrl.startsWith('http') ? config.currentUrl : 'https://' + config.currentUrl);
                } catch (e) {
                    errors.push('网站地址格式不正确');
                }
            }

            if (!config.nickname || !config.nickname.trim()) {
                errors.push('昵称不能为空');
            }

            if (config.username && config.username.length > 100) {
                errors.push('用户名长度不能超过100个字符');
            }

            if (config.password && config.password.length > 100) {
                errors.push('密码长度不能超过100个字符');
            }

            return {
                valid: errors.length === 0,
                errors: errors
            };
        },
        /**
         * 获取所有配置
         * @returns {Array} 配置数组
         */
        getAllConfigs() {
            try {
                const keys = GM_listValues();
                return keys.filter(k => k.startsWith(this.CONFIG_PREFIX)).map(k => {
                    const data = GM_getValue(k);
                    // 检查数据是否有效
                    if (!data || typeof data !== 'object') {
                        console.warn('getAllConfigs: 跳过无效配置', k, data);
                        return null;
                    }

                    // 升级配置到最新版本
                    const upgradedData = this.upgradeConfig(data);
                    if (!upgradedData) {
                        console.warn('getAllConfigs: 配置升级失败,跳过', k);
                        return null;
                    }

                    if (upgradedData !== data) {
                        GM_setValue(k, upgradedData);
                    }
                    return { key: k, data: upgradedData };
                }).filter(item => item !== null); // 过滤掉无效的配置
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.readFailed, e);
                return [];
            }
        },
        /**
         * 获取指定配置
         * @param {string} key 配置键
         * @returns {Object} 配置对象
         */
        getConfig(key) {
            try {
                const config = GM_getValue(key);
                // 检查配置是否存在
                if (!config) {
                    return null;
                }

                // 升级配置到最新版本
                const upgradedConfig = this.upgradeConfig(config);
                if (!upgradedConfig) {
                    console.warn('getConfig: 配置升级失败', key);
                    return null;
                }
                return upgradedConfig;
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.readFailed, e);
                return null;
            }
        },
        /**
         * 保存配置
         * @param {Object} config 配置对象
         * @param {string} masterPassword 主密码(可选)
         * @returns {string} 配置键
         */
        async saveConfig(config, masterPassword = null) {
            // 验证配置
            const validation = this.validateConfig(config);
            if (!validation.valid) {
                Utils.showError(validation.errors.join('\n'));
                return null;
            }

            // 如果提供了主密码且密码未加密,则加密密码
            if (masterPassword && config.password && !Utils.isPasswordEncrypted(config.password)) {
                try {
                    config.password = await Utils.encryptPassword(config.password, masterPassword);
                } catch (error) {
                    console.warn('密码加密失败,使用明文存储:', error);
                }
            }

            // 检查是否为新建配置
            const isNewConfig = !config.createdAt || !config.updatedAt;

            // 添加版本信息
            config.version = this.CONFIG_VERSION;

            // 时间戳处理
            if (isNewConfig) {
                // 新建配置:设置创建时间和更新时间
                config.createdAt = new Date().toISOString();
                config.updatedAt = new Date().toISOString();
            } else {
                // 更新配置:只更新修改时间,保持创建时间不变
                config.updatedAt = new Date().toISOString();
            }

            try {
                const key = this.CONFIG_PREFIX + config.currentUrl;
                GM_setValue(key, config);
                return key;
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.saveFailed, e);
                return null;
            }
        },
        /**
         * 删除配置
         * @param {string} key 配置键
         */
        deleteConfig(key) {
            try {
                GM_deleteValue(key);
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.deleteFailed, e);
            }
        },
        /**
         * 获取配置(带解密)
         * @param {string} key 配置键
         * @param {string} masterPassword 主密码(可选)
         * @returns {Object} 配置对象
         */
        async getConfigDecrypted(key, masterPassword = null) {
            try {
                const config = GM_getValue(key);
                if (!config) return null;

                // 检查配置是否为有效对象
                if (typeof config !== 'object') {
                    console.warn('getConfigDecrypted: 配置不是有效对象', key, config);
                    return null;
                }

                // 升级配置到最新版本
                const upgradedConfig = this.upgradeConfig(config);
                if (!upgradedConfig) {
                    console.warn('getConfigDecrypted: 配置升级失败', key);
                    return null;
                }

                // 如果提供了主密码且密码已加密,则解密密码
                if (masterPassword && upgradedConfig.password && Utils.isPasswordEncrypted(upgradedConfig.password)) {
                    try {
                        upgradedConfig.password = await Utils.decryptPassword(upgradedConfig.password, masterPassword);
                    } catch (error) {
                        console.warn('密码解密失败:', error);
                        // 解密失败时保持加密状态
                    }
                }

                return upgradedConfig;
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.readFailed, e);
                return null;
            }
        }
    };
    // UI渲染模块
    const UIRenderer = {
        /**
         * 渲染配置列表
         * @param {Array} configs 配置数组
         * @param {string} activeKey 当前选中的配置键
         * @param {string} filter 搜索过滤条件
         */
        renderConfigList(configs, activeKey, filter = '') {
            const list = document.getElementById('config-list');
            list.innerHTML = '';
            let filtered = configs;
            if (filter) {
                const kw = filter.trim().toLowerCase();
                filtered = configs.filter(c =>
                    (c.data.nickname || '').toLowerCase().includes(kw) ||
                    (c.data.currentUrl || '').toLowerCase().includes(kw)
                );
            }
            if (filtered.length === 0) {
                list.innerHTML = '<div style="color:#aaa;padding:20px 0;text-align:center;">无匹配配置</div>';
                return;
            }

            // 获取当前网页URL(不含查询参数和hash)
            const currentUrl = window.location.origin + window.location.pathname;

            // 对配置进行排序:匹配当前网页的配置优先显示
            filtered.sort((a, b) => {
                const aMatchesCurrent = a.data.currentUrl === currentUrl;
                const bMatchesCurrent = b.data.currentUrl === currentUrl;

                // 如果a匹配当前网页而b不匹配,a排在前面
                if (aMatchesCurrent && !bMatchesCurrent) return -1;
                // 如果b匹配当前网页而a不匹配,b排在前面
                if (!aMatchesCurrent && bMatchesCurrent) return 1;

                // 如果都匹配或都不匹配,按更新时间排序(新的在前)
                const aUpdated = a.data.updatedAt ? new Date(a.data.updatedAt).getTime() : 0;
                const bUpdated = b.data.updatedAt ? new Date(b.data.updatedAt).getTime() : 0;
                return bUpdated - aUpdated;
            });

            filtered.forEach(c => {
                const div = document.createElement('div');
                let className = 'config-item';
                if (c.key === activeKey) className += ' active';

                // 检查是否匹配当前网页
                const matchesCurrent = c.data.currentUrl === currentUrl;
                if (matchesCurrent) className += ' current-page';

                div.className = className;
                div.dataset.key = c.key;

                // 只显示到网址部分(不显示路径后的内容)
                let urlShow = '';
                try {
                    const u = new URL(c.data.currentUrl);
                    urlShow = u.origin;
                } catch (e) {
                    urlShow = c.data.currentUrl || '';
                }

                // 为匹配当前网页的配置添加特殊样式
                const currentIndicator = matchesCurrent ? '<span style="color:#4d7cfe;font-weight:600;margin-right:4px;">&#128205;</span>' : '';
                const nicknameStyle = matchesCurrent ? 'style="color:#4d7cfe;font-weight:600;"' : '';

                div.innerHTML = `
                    <div class="nickname" ${nicknameStyle}>
                        ${currentIndicator}${Utils.escapeHTML(c.data.nickname || '未命名')}
                    </div>
                    <div class="url">${Utils.escapeHTML(urlShow)}</div>
                `;
                list.appendChild(div);
            });
        },
        /**
         * 将配置加载到表单
         * @param {Object} config 配置对象
         */
        async loadConfigToForm(config) {
            document.getElementById('edit-url').value = config.currentUrl || '';
            document.getElementById('edit-nickname').value = config.nickname || '';
            document.getElementById('edit-username').value = config.username || '';

            // 处理密码解密
            let displayPassword = config.password || '';
            if (Utils.isPasswordEncrypted(config.password)) {
                try {
                    displayPassword = await Utils.decryptPassword(config.password, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                } catch (error) {
                    console.warn('密码解密失败,显示加密状态:', error);
                    displayPassword = config.password; // 解密失败时显示加密的密码
                }
            }
            document.getElementById('edit-password').value = displayPassword;

            document.getElementById('edit-username-selector').value = config.selectors?.username || '';
            document.getElementById('edit-password-selector').value = config.selectors?.password || '';
            document.getElementById('edit-login-button-selector').value = config.selectors?.loginButton || '';
            // 处理先手点击按钮选择器(数组转字符串)
            const preClickButtons = config.selectors?.preClickButtons || [];
            document.getElementById('edit-pre-click-selector').value = Array.isArray(preClickButtons) ? preClickButtons.join(', ') : preClickButtons;
            document.getElementById('edit-autoLogin').checked = !!config.autoLogin;
            document.getElementById('edit-autoFill').checked = !!config.autoFill;

            // 显示密码加密状态
            const passwordInput = document.getElementById('edit-password');
            if (passwordInput && Utils.isPasswordEncrypted(config.password)) {
                passwordInput.style.borderColor = '#28a745';
                passwordInput.style.backgroundColor = '#f8fff9';
                passwordInput.title = '密码已加密存储';
            } else {
                passwordInput.style.borderColor = '';
                passwordInput.style.backgroundColor = '';
                passwordInput.title = '';
            }

            // 切换配置时不自动展开选择器区域,保持当前折叠状态
            const section = document.getElementById('selectors-section');
            const btn = document.getElementById('toggle-selectors-btn');
            const listArea = document.querySelector('.config-list-area');
            if (section && btn) {
                section.style.display = selectorsCollapsed ? 'none' : '';
                btn.textContent = selectorsCollapsed ? '▲' : '▼';
                // 同步调整左侧列表高度
                if (listArea) {
                    listArea.style.height = selectorsCollapsed ? CONSTANTS.PANEL_DIMENSIONS.height.collapsed : CONSTANTS.PANEL_DIMENSIONS.height.expanded;
                }
            }
            // 更新跳转按钮显示状态
            updateGotoBtnVisibility();
        },
        /**
         * 从表单获取配置数据
         * @returns {Object} 配置对象
         */
        getConfigFromForm() {
            // 处理先手点击按钮选择器(字符串转数组)
            const preClickSelectorStr = document.getElementById('edit-pre-click-selector').value.trim();
            const preClickButtons = preClickSelectorStr ? preClickSelectorStr.split(',').map(s => s.trim()).filter(s => s) : [];

            return {
                currentUrl: document.getElementById('edit-url').value.trim(),
                nickname: document.getElementById('edit-nickname').value.trim(),
                username: document.getElementById('edit-username').value.trim(),
                password: document.getElementById('edit-password').value.trim(),
                autoLogin: document.getElementById('edit-autoLogin').checked,
                autoFill: document.getElementById('edit-autoFill').checked,
                selectors: {
                    username: document.getElementById('edit-username-selector').value.trim(),
                    password: document.getElementById('edit-password-selector').value.trim(),
                    loginButton: document.getElementById('edit-login-button-selector').value.trim(),
                    preClickButtons: preClickButtons
                }
            };
        }
    };
    // 事件绑定模块
    const EventBinder = {
        currentKey: null,
        allConfigs: [],
        /**
         * 绑定所有事件监听器
         */
        bindAll() {
            // 搜索
            document.getElementById('config-search').addEventListener('input', e => {
                UIRenderer.renderConfigList(this.allConfigs, this.currentKey, e.target.value);
            });
            // 列表点击
            document.getElementById('config-list').addEventListener('click', async (e) => {
                const item = e.target.closest('.config-item');
                if (!item) return;
                this.currentKey = item.dataset.key;

                // 使用CONSTANTS中的主密码
                const masterPassword = CONSTANTS.ENCRYPTION.MASTER_PASSWORD;

                // 获取配置(带解密)
                const config = await DataManager.getConfigDecrypted(this.currentKey, masterPassword);
                if (config) await UIRenderer.loadConfigToForm(config);
                UIRenderer.renderConfigList(this.allConfigs, this.currentKey, document.getElementById('config-search').value);
            });
            // 新建
            document.getElementById('new-config-btn').addEventListener('click', async () => {
                const template = DataManager.getConfigTemplate('default');
                template.currentUrl = window.location.origin + window.location.pathname;
                template.nickname = document.title || '新配置';
                await UIRenderer.loadConfigToForm(template);
                this.currentKey = null;
                UIRenderer.renderConfigList(this.allConfigs, this.currentKey, document.getElementById('config-search').value);
                // 新建时自动展开选择器区域
                selectorsCollapsed = false;
                const section = document.getElementById('selectors-section');
                const btn = document.getElementById('toggle-selectors-btn');
                const listArea = document.querySelector('.config-list-area');
                if (section && btn) {
                    section.style.display = '';
                    btn.textContent = '▼';
                    // 同步调整左侧列表高度
                    if (listArea) {
                        listArea.style.height = CONSTANTS.PANEL_DIMENSIONS.height.expanded;
                    }
                }
                // 更新跳转按钮显示状态
                updateGotoBtnVisibility();
            });
            // 保存
            document.getElementById('save-config-btn').addEventListener('click', async () => {
                const config = UIRenderer.getConfigFromForm();
                if (!config.currentUrl) return Utils.showError(CONSTANTS.MESSAGES.errors.urlRequired);
                if (!config.nickname) return Utils.showError(CONSTANTS.MESSAGES.errors.nicknameRequired);

                // 如果是更新已有配置,保留原有的时间戳信息
                if (this.currentKey) {
                    try {
                        const existingConfig = DataManager.getConfig(this.currentKey);
                        if (existingConfig) {
                            config.createdAt = existingConfig.createdAt;
                            config.updatedAt = existingConfig.updatedAt;
                            config.version = existingConfig.version;
                        }
                    } catch (e) {
                        console.warn('获取原有配置失败,将作为新配置处理:', e);
                    }
                }

                // 使用CONSTANTS中的主密码
                const masterPassword = CONSTANTS.ENCRYPTION.MASTER_PASSWORD;

                // 保存配置(带加密)
                const key = await DataManager.saveConfig(config, masterPassword);
                this.currentKey = key;
                this.allConfigs = DataManager.getAllConfigs();
                UIRenderer.renderConfigList(this.allConfigs, this.currentKey, document.getElementById('config-search').value);
                Utils.showMsg(CONSTANTS.MESSAGES.success.configSaved);
            });
            // 删除
            document.getElementById('delete-config-btn').addEventListener('click', async () => {
                if (!this.currentKey) return Utils.showError(CONSTANTS.MESSAGES.errors.configRequired);
                Utils.showConfirm(CONSTANTS.MESSAGES.confirm.deleteConfig, async () => {
                    DataManager.deleteConfig(this.currentKey);
                    this.currentKey = null;
                    this.allConfigs = DataManager.getAllConfigs();
                    UIRenderer.renderConfigList(this.allConfigs, this.currentKey, document.getElementById('config-search').value);
                    await UIRenderer.loadConfigToForm({});
                    Utils.showMsg(CONSTANTS.MESSAGES.success.configDeleted);
                });
            });
        },
        /**
         * 初始化事件绑定器
         */
        async init() {
            this.allConfigs = DataManager.getAllConfigs();
            // 优先根据当前网址自动选中配置
            const currentUrl = window.location.origin + window.location.pathname;
            const matchKey = this.allConfigs.find(c => c.data.currentUrl === currentUrl)?.key;
            this.currentKey = matchKey || null;
            UIRenderer.renderConfigList(this.allConfigs, this.currentKey, '');
            if (this.currentKey) {
                // 使用CONSTANTS中的主密码
                const masterPassword = CONSTANTS.ENCRYPTION.MASTER_PASSWORD;

                // 获取配置(带解密)
                const config = await DataManager.getConfigDecrypted(this.currentKey, masterPassword);
                if (config) await UIRenderer.loadConfigToForm(config);
            } else {
                // 如果当前网页没有配置信息,自动触发新建功能
                const template = DataManager.getConfigTemplate('default');
                template.currentUrl = currentUrl;
                template.nickname = document.title || '新配置';
                await UIRenderer.loadConfigToForm(template);
                // 新建时自动展开选择器区域
                selectorsCollapsed = false;
                const section = document.getElementById('selectors-section');
                const btn = document.getElementById('toggle-selectors-btn');
                const listArea = document.querySelector('.config-list-area');
                if (section && btn) {
                    section.style.display = '';
                    btn.textContent = '▼';
                    // 同步调整左侧列表高度
                    if (listArea) {
                        listArea.style.height = CONSTANTS.PANEL_DIMENSIONS.height.expanded;
                    }
                }
                // 更新跳转按钮显示状态
                updateGotoBtnVisibility();
            }
            this.bindAll();
        }
    };
    // 工具函数模块
    const Utils = {
        /**
         * 显示错误提示弹窗
         * @param {string} msg 错误消息
         * @param {Error} err 错误对象
         */
        showError(msg, err) {
            // 创建错误提示弹窗
            const errorDialog = document.createElement('div');
            errorDialog.style.cssText = `
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: #fff;
                padding: 20px;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.3);
                z-index: 99999999;
                max-width: 400px;
                border: 2px solid #e74c3c;
                `;
            errorDialog.innerHTML = `
                <div style="display:flex;align-items:center;gap:10px;margin-bottom:15px;">
                    <span style="font-size:20px;">&#10060;</span>
                    <h3 style="margin:0;color:#e74c3c;">错误</h3>
                </div>
                <p style="margin:0 0 15px 0;color:#333;">${msg}</p>
                <button onclick="this.parentElement.remove()" style="
                    background:#e74c3c;
                    color:white;
                    border:none;
                    padding:8px 16px;
                    border-radius:4px;
                    cursor:pointer;
                ">确定</button>
            `;
            document.body.appendChild(errorDialog);
            if (err) console.error(msg, err);
        },
        /**
         * 显示消息提示
         * @param {string} msg 消息内容
         * @param {string} type 消息类型
         */
        showMsg(msg, type = 'success') {
            // 创建成功提示弹窗
            const msgDialog = document.createElement('div');
            const isSuccess = type === 'success';
            const color = isSuccess ? '#2ecc71' : '#3498db';
            const icon = isSuccess ? '&#9989;' : '&#8505;&#65039;';
            msgDialog.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: #fff;
                padding: 15px 20px;
                border-radius: 6px;
                box-shadow: 0 4px 15px rgba(0,0,0,0.2);
                z-index: 99999999;
                border-left: 4px solid ${color};
                animation: slideIn 0.3s ease;
                `;
            msgDialog.innerHTML = `
                <div style="display:flex;align-items:center;gap:8px;">
                    <span style="font-size:16px;">${icon}</span>
                    <span style="color:#333;">${msg}</span>
                </div>
            `;
            document.body.appendChild(msgDialog);
            // 3秒后自动消失
            setTimeout(() => {
                if (msgDialog.parentElement) {
                    msgDialog.style.animation = 'slideOut 0.3s ease';
                    msgDialog.addEventListener('animationend', () => {
                        if (msgDialog.parentElement) {
                            msgDialog.remove();
                        }
                    }, { once: true });
                }
            }, 3000);
        },
        /**
         * 显示确认对话框
         * @param {string} msg 确认消息
         * @param {Function} onConfirm 确认回调
         * @param {Function} onCancel 取消回调
         */
        showConfirm(msg, onConfirm, onCancel) {
            // 创建确认对话框
            const confirmDialog = document.createElement('div');
            confirmDialog.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100vw;
                height: 100vh;
                background: rgba(0,0,0,0.5);
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 99999999;
            `;
            confirmDialog.innerHTML = `
                <div style="
                    background: #fff;
                    padding: 25px;
                    border-radius: 8px;
                    max-width: 400px;
                    text-align: center;
                ">
                    <div style="font-size:24px;margin-bottom:15px;">&#9888;&#65039;</div>
                    <p style="margin:0 0 20px 0;color:#333;">${msg}</p>
                    <div style="display:flex;gap:10px;justify-content:center;">
                    <button id="confirm-yes" style="
                        background: #e74c3c;
                        color: white;
                        border: none;
                        padding: 8px 16px;
                        border-radius: 4px;
                        cursor: pointer;
                    ">确定</button>
                    <button id="confirm-no" style="
                        background: #95a5a6;
                        color: white;
                        border: none;
                        padding: 8px 16px;
                        border-radius: 4px;
                        cursor: pointer;
                    ">取消</button>
                    </div>
                </div>
            `;
            document.body.appendChild(confirmDialog);

            // 绑定事件
            confirmDialog.querySelector('#confirm-yes').onclick = () => {
                confirmDialog.remove();
                if (onConfirm) onConfirm();
            };
            confirmDialog.querySelector('#confirm-no').onclick = () => {
                confirmDialog.remove();
                if (onCancel) onCancel();
            };

            // 将焦点设置到确认按钮上
            setTimeout(() => {
                const confirmBtn = confirmDialog.querySelector('#confirm-yes');
                if (confirmBtn) {
                    confirmBtn.focus();
                }
            }, 300);
        },
        /**
         * HTML转义
         * @param {string} str 原始字符串
         * @returns {string} 转义后的字符串
         */
        escapeHTML(str) {
            return (str || '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
        },
        /**
         * 防抖函数
         * @param {Function} fn 要防抖的函数
         * @param {number} delay 延迟时间
         * @returns {Function} 防抖后的函数
         */
        debounce(fn, delay) {
            let timer = null;
            return function (...args) {
                clearTimeout(timer);
                timer = setTimeout(() => fn.apply(this, args), delay);
            };
        },
        /**
         * 高亮匹配的元素
         * @param {string} selector CSS选择器
         * @param {boolean} checkPopup 是否检测弹窗元素
         * @returns {number} 匹配的元素数量
         */
        highlightElements(selector, checkPopup = true) {
            let elements;
            try {
                elements = document.querySelectorAll(selector);
            } catch (e) {
                return 0;
            }
            if (!elements.length) return 0;

            // 如果需要检测弹窗元素,过滤掉弹窗内的元素
            if (checkPopup) {
                elements = Array.from(elements).filter(el => !this.isPopupElement(el));
                if (elements.length === 0) return 0;
            }

            const original = [];
            elements.forEach(el => {
                original.push({ el, bg: el.style.backgroundColor, outline: el.style.outline });
                el.style.backgroundColor = 'rgba(255,255,0,0.3)';
                el.style.outline = '2px solid #ff9800';
            });
            setTimeout(() => {
                original.forEach(({ el, bg, outline }) => {
                    el.style.backgroundColor = bg;
                    el.style.outline = outline;
                });
            }, CONSTANTS.ANIMATION.highlightDuration);
            return elements.length;
        },
        /**
         * 检查元素是否为弹窗
         * @param {Element} element DOM元素
         * @returns {boolean} 是否为弹窗元素
         */
        isPopupElement(element) {
            // 辅助函数:检查元素是否匹配选择器
            const matchesSelector = (el, selectors) => {
                return selectors.some(selector => el.matches(selector));
            };

            // 辅助函数:检查文本是否包含关键词
            const containsKeywords = (text, keywords) => {
                return keywords.some(keyword => text.toLowerCase().includes(keyword));
            };

            // 辅助函数:检查元素样式是否为弹窗
            const isPopupStyle = (el) => {
                const style = window.getComputedStyle(el);
                const position = style.position;
                const zIndex = parseInt(style.zIndex) || 0;
                return position === 'fixed' && zIndex > CONSTANTS.POPUP.minZIndex;
            };

            // 检查元素本身
            if (matchesSelector(element, CONSTANTS.POPUP.selectors)) {
                return true;
            }

            // 检查元素的class和id
            const className = element.className || '';
            const id = element.id || '';

            if (containsKeywords(className, CONSTANTS.POPUP.keywords) || containsKeywords(id, CONSTANTS.POPUP.keywords)) {
                return true;
            }

            // 检查元素的样式
            if (isPopupStyle(element)) {
                return true;
            }

            // 检查父元素是否为弹窗
            let parent = element.parentElement;
            while (parent && parent !== document.body) {
                if (matchesSelector(parent, CONSTANTS.POPUP.selectors) || isPopupStyle(parent)) {
                    return true;
                }

                parent = parent.parentElement;
            }

            return false;
        },
        /**
         * 生成加密密钥
         * @param {string} password 用户密码
         * @returns {Promise<CryptoKey>} 加密密钥
         */
        async generateEncryptionKey(password) {
            try {
                // 将密码和盐值组合
                const passwordWithSalt = password + CONSTANTS.ENCRYPTION.SALT;

                // 将字符串转换为ArrayBuffer
                // 检查 crypto.subtle 是否可用
                if (!window.crypto || !window.crypto.subtle) {
                    throw new Error('当前环境不支持 Web Crypto API,无法生成加密密钥');
                }

                const encoder = new TextEncoder();
                const passwordBuffer = encoder.encode(passwordWithSalt);

                // 使用PBKDF2派生密钥
                const baseKey = await crypto.subtle.importKey(
                    'raw',
                    passwordBuffer,
                    { name: 'PBKDF2' },
                    false,
                    ['deriveBits', 'deriveKey']
                );

                // 派生加密密钥
                const encryptionKey = await crypto.subtle.deriveKey(
                    {
                        name: 'PBKDF2',
                        salt: encoder.encode(CONSTANTS.ENCRYPTION.SALT),
                        iterations: CONSTANTS.ENCRYPTION.KEY_DERIVATION.iterations,
                        hash: 'SHA-256'
                    },
                    baseKey,
                    { name: CONSTANTS.ENCRYPTION.ALGORITHM, length: CONSTANTS.ENCRYPTION.KEY_DERIVATION.keyLength },
                    false,
                    ['encrypt', 'decrypt']
                );

                return encryptionKey;
            } catch (error) {
                console.error('生成加密密钥失败:', error);
                throw new Error('加密密钥生成失败');
            }
        },
        /**
         * 加密密码
         * @param {string} password 原始密码
         * @param {string} masterPassword 主密码
         * @returns {Promise<string>} 加密后的密码
         */
        async encryptPassword(password, masterPassword) {
            try {
                if (!password || !masterPassword) {
                    return password; // 如果没有密码或主密码,返回原密码
                }

                const key = await this.generateEncryptionKey(masterPassword);
                const encoder = new TextEncoder();
                const passwordBuffer = encoder.encode(password);

                // 生成随机IV
                const iv = crypto.getRandomValues(new Uint8Array(12));

                // 加密数据
                const encryptedData = await crypto.subtle.encrypt(
                    {
                        name: CONSTANTS.ENCRYPTION.ALGORITHM,
                        iv: iv
                    },
                    key,
                    passwordBuffer
                );

                // 将IV和加密数据组合并转换为Base64
                const combined = new Uint8Array(iv.length + encryptedData.byteLength);
                combined.set(iv);
                combined.set(new Uint8Array(encryptedData), iv.length);

                const encryptedString = btoa(String.fromCharCode(...combined));
                return CONSTANTS.ENCRYPTION.ENCRYPTED_PREFIX + encryptedString;
            } catch (error) {
                console.error('密码加密失败:', error);
                return password; // 加密失败时返回原密码
            }
        },
        /**
         * 解密密码
         * @param {string} encryptedPassword 加密的密码
         * @param {string} masterPassword 主密码
         * @returns {Promise<string>} 解密后的密码
         */
        async decryptPassword(encryptedPassword, masterPassword) {
            try {
                if (!encryptedPassword || !masterPassword || !encryptedPassword.startsWith(CONSTANTS.ENCRYPTION.ENCRYPTED_PREFIX)) {
                    return encryptedPassword; // 如果不是加密密码,返回原密码
                }

                let key;
                try {
                    key = await this.generateEncryptionKey(masterPassword);
                } catch (error) {
                    console.warn(CONSTANTS.MESSAGES.errors.cryptoUnsupported);
                    return encryptedPassword; // 返回原始加密字符串
                }
                const encryptedString = encryptedPassword.substring(CONSTANTS.ENCRYPTION.ENCRYPTED_PREFIX.length);

                // 从Base64解码
                const combined = new Uint8Array(atob(encryptedString).split('').map(char => char.charCodeAt(0)));

                // 提取IV和加密数据
                const iv = combined.slice(0, 12);
                const encryptedData = combined.slice(12);

                // 解密数据
                const decryptedData = await crypto.subtle.decrypt(
                    {
                        name: CONSTANTS.ENCRYPTION.ALGORITHM,
                        iv: iv
                    },
                    key,
                    encryptedData
                );

                const decoder = new TextDecoder();
                return decoder.decode(decryptedData);
            } catch (error) {
                console.error('密码解密失败:', error);
                throw error; // 解密失败时抛出错误
            }
        },
        /**
         * 检查密码是否已加密
         * @param {string} password 密码
         * @returns {boolean} 是否已加密
         */
        isPasswordEncrypted(password) {
            return password && password.startsWith(CONSTANTS.ENCRYPTION.ENCRYPTED_PREFIX);
        },
        /**
         * 安全地添加事件监听器,并记录到清理数组
         * @param {Element} element 目标元素
         * @param {string} type 事件类型
         * @param {Function} handler 事件处理函数
         * @param {Object} options 事件选项
         */
        addSafeEventListener(element, type, handler, options = {}) {
            element.addEventListener(type, handler, options);

            // 记录到清理数组
            if (window.loginManagerEventListeners) {
                window.loginManagerEventListeners.push({
                    element: element,
                    type: type,
                    handler: handler,
                    options: options
                });
            }
        },
        /**
         * 安全地添加文档级别的事件监听器
         * @param {string} type 事件类型
         * @param {Function} handler 事件处理函数
         * @param {Object} options 事件选项
         */
        addDocumentEventListener(type, handler, options = {}) {
            document.addEventListener(type, handler, options);

            // 记录到清理数组
            if (window.loginManagerEventListeners) {
                window.loginManagerEventListeners.push({
                    element: document,
                    type: type,
                    handler: handler,
                    options: options
                });
            }
        }
    };

    // ========== 先手点击执行函数 ==========
    /**
     * 执行先手点击操作
     * @param {Object} config 配置对象
     */
    async function executePreClickActions(config) {
        const preClickButtons = config.selectors?.preClickButtons || [];
        if (!Array.isArray(preClickButtons) || preClickButtons.length === 0) {
            console.log('&#128269; 无需执行先手点击操作');
            return;
        }

        console.log(`&#127919; 开始执行先手点击操作,共 ${preClickButtons.length} 个按钮`);

        for (let i = 0; i < preClickButtons.length; i++) {
            const selector = preClickButtons[i].trim();
            if (!selector) continue;

            try {
                const button = document.querySelector(selector);
                if (button) {
                    console.log(`&#9989; 找到先手点击按钮 [${i + 1}/${preClickButtons.length}]: ${selector}`);

                    // 高亮显示要点击的按钮
                    const originalStyle = button.style.outline;
                    button.style.outline = '2px solid #ff6b6b';
                    button.style.outlineOffset = '2px';

                    // 点击按钮
                    button.click();
                    console.log(`&#128433;&#65039; 已点击按钮: ${selector}`);

                    // 恢复按钮样式
                    setTimeout(() => {
                        button.style.outline = originalStyle;
                        button.style.outlineOffset = '';
                    }, 1000);

                    // 等待一段时间让页面响应
                    if (i < preClickButtons.length - 1) {
                        await new Promise(resolve => setTimeout(resolve, CONSTANTS.ANIMATION.preClickDelay));
                    }
                } else {
                    console.warn(`&#9888;&#65039; 未找到先手点击按钮 [${i + 1}/${preClickButtons.length}]: ${selector}`);
                }
            } catch (error) {
                console.error(`&#10060; 先手点击按钮失败 [${i + 1}/${preClickButtons.length}]: ${selector}`, error);
            }
        }

        console.log('&#9989; 先手点击操作执行完成');
    }

    // ========== 选择器验证与实时预览 ==========
    /**
     * 设置选择器验证功能
     */
    function setupSelectorValidation() {
        const selectorFields = [
            { input: 'edit-username-selector', btn: 'check-username-selector' },
            { input: 'edit-password-selector', btn: 'check-password-selector' },
            { input: 'edit-login-button-selector', btn: 'check-login-button-selector' },
            { input: 'edit-pre-click-selector', btn: 'check-pre-click-selector' }
        ];
        selectorFields.forEach(({ input, btn }) => {
            const inputEl = document.getElementById(input);
            const btnEl = document.getElementById(btn);
            // 点击按钮验证
            btnEl.addEventListener('click', () => {
                const selector = inputEl.value.trim();
                if (!selector) {
                    btnEl.textContent = '&#10060;';
                    btnEl.style.color = 'red';
                    return;
                }

                // 特殊处理先手点击选择器(支持多个选择器)
                if (input === 'edit-pre-click-selector') {
                    const selectors = selector.split(',').map(s => s.trim()).filter(s => s);
                    let totalCount = 0;
                    let foundCount = 0;

                    selectors.forEach((sel, index) => {
                        try {
                            const count = Utils.highlightElements(sel, false);
                            totalCount += count;
                            if (count > 0) foundCount++;
                            console.log(`先手点击选择器 [${index + 1}]: ${sel} - 找到 ${count} 个元素`);
                        } catch (e) {
                            console.warn(`先手点击选择器 [${index + 1}] 验证失败: ${sel}`, e);
                        }
                    });

                    if (foundCount === selectors.length && totalCount > 0) {
                        btnEl.textContent = '&#9989;';
                        btnEl.style.color = 'green';
                    } else if (foundCount > 0) {
                        btnEl.textContent = '&#9888;&#65039;';
                        btnEl.style.color = 'orange';
                    } else {
                        btnEl.textContent = '&#10060;';
                        btnEl.style.color = 'red';
                    }
                    return;
                }

                // 普通选择器验证
                let count = 0;
                try {
                    // 验证时不检测弹窗元素,允许验证弹窗内的元素
                    count = Utils.highlightElements(selector, false);
                } catch (e) {
                    count = 0;
                }
                if (count > 0) {
                    btnEl.textContent = '&#9989;';
                    btnEl.style.color = 'green';
                } else {
                    btnEl.textContent = '&#10060;';
                    btnEl.style.color = 'red';
                }
            });
            // 输入时实时预览(防抖)
            inputEl.addEventListener('input', Utils.debounce(function () {
                btnEl.textContent = '&#128260;';
                btnEl.style.color = '';
                const selector = inputEl.value.trim();
                if (!selector) return;

                // 特殊处理先手点击选择器(支持多个选择器)
                if (input === 'edit-pre-click-selector') {
                    const selectors = selector.split(',').map(s => s.trim()).filter(s => s);
                    selectors.forEach(sel => {
                        try {
                            Utils.highlightElements(sel, false);
                        } catch (e) {
                            // 忽略预览错误
                        }
                    });
                    return;
                }

                // 普通选择器预览
                // 预览时不检测弹窗元素,允许预览弹窗内的元素
                Utils.highlightElements(selector, false);
            }, CONSTANTS.ANIMATION.debounceDelay));
        });
    }

    // ========== 选择器折叠功能 ==========
    let selectorsCollapsed = true; // 折叠状态全局变量,默认闭合
    /**
     * 设置选择器区域折叠功能
     */
    function setupSelectorCollapse() {
        const btn = document.getElementById('toggle-selectors-btn');
        const section = document.getElementById('selectors-section');
        const row = document.querySelector('.selector-toggle-row');
        const listArea = document.querySelector('.config-list-area');
        // 初始化状态(每次都强制刷新)
        section.style.display = selectorsCollapsed ? 'none' : '';
        btn.textContent = selectorsCollapsed ? '▲' : '▼';
        // 设置初始高度
        if (listArea) {
            listArea.style.height = selectorsCollapsed ? CONSTANTS.PANEL_DIMENSIONS.height.collapsed : CONSTANTS.PANEL_DIMENSIONS.height.expanded;
        }
        // 绑定整行点击
        if (row) {
            // 移除所有现有事件监听器
            const newRow = row.cloneNode(true);
            row.parentNode.replaceChild(newRow, row);

            // 重新绑定事件
            newRow.addEventListener('click', function (e) {
                // 避免label选中文字时误触
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
                selectorsCollapsed = !selectorsCollapsed;
                section.style.display = selectorsCollapsed ? 'none' : '';
                btn.textContent = selectorsCollapsed ? '▲' : '▼';
                // 动态调整左侧列表高度
                if (listArea) {
                    listArea.style.height = selectorsCollapsed ? CONSTANTS.PANEL_DIMENSIONS.height.collapsed : CONSTANTS.PANEL_DIMENSIONS.height.expanded;
                }
            });
        }
    }

    // ========== 密码显示/隐藏切换 ==========
    /**
     * 设置密码显示/隐藏切换功能
     */
    function setupPasswordToggle() {
        const pwdInput = document.getElementById('edit-password');
        const toggleBtn = document.getElementById('toggle-edit-password');
        if (!pwdInput || !toggleBtn) return;

        // 克隆按钮并替换原有按钮,确保事件监听器被完全清除
        const newToggleBtn = toggleBtn.cloneNode(true);
        toggleBtn.parentNode.replaceChild(newToggleBtn, toggleBtn);

        newToggleBtn.addEventListener('click', function () {
            if (pwdInput.type === 'password') {
                pwdInput.type = 'text';
                newToggleBtn.textContent = '&#128584;';
            } else {
                pwdInput.type = 'password';
                newToggleBtn.textContent = '&#128065;';
            }
        });
    }



    // ========== 导入/导出全部配置 ==========
    /**
     * 设置导入导出功能
     * @param {Object} EventBinder 事件绑定器对象
     */
    function setupImportExport(EventBinder) {
        // 创建导入对话框
        let importDialog = document.getElementById('import-dialog');
        if (!importDialog) {
            importDialog = document.createElement('div');
            importDialog.id = 'import-dialog';
            importDialog.style.display = 'none';
            importDialog.style.position = 'fixed';
            importDialog.style.top = '0';
            importDialog.style.left = '0';
            importDialog.style.width = '100vw';
            importDialog.style.height = '100vh';
            importDialog.style.background = 'rgba(0,0,0,0.4)';
            importDialog.style.zIndex = '100000';
            importDialog.innerHTML = `
                <div style="
                    background:#fff;
                    max-width:450px;
                    margin:10vh auto;
                    padding:25px;
                    border-radius:10px;
                    box-shadow:0 8px 30px rgba(0,0,0,0.3);
                    border:1px solid #e0e0e0;
                ">
                    <div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;">
                    <span style="font-size:20px;">&#128229;</span>
                    <h3 style="margin:0;color:#333;font-size:18px;">导入配置</h3>
                    </div>
                    <p style="margin:0 0 15px 0;color:#666;font-size:14px;">请粘贴导出的配置JSON数据:</p>
                    <textarea id="import-textarea" placeholder="在此粘贴JSON配置数据..." style="
                    width:100%;
                    height:180px;
                    padding:12px;
                    border:2px solid #3498db;
                    border-radius:6px;
                    font-family:'Courier New',monospace;
                    font-size:13px;
                    resize:vertical;
                    box-sizing:border-box;
                    outline:none;
                    "></textarea>
                    <div style="display:flex;gap:10px;justify-content:flex-end;margin-top:20px;">
                    <button id="confirm-import" style="
                        background:linear-gradient(to right,#2ecc71,#27ae60);
                        color:white;
                        border:none;
                        padding:10px 20px;
                        border-radius:6px;
                        cursor:pointer;
                        font-weight:600;
                        transition:all 0.2s;
                    ">确认导入</button>
                    <button id="cancel-import" style="
                        background:linear-gradient(to right,#e74c3c,#c0392b);
                        color:white;
                        border:none;
                        padding:10px 20px;
                        border-radius:6px;
                        cursor:pointer;
                        font-weight:600;
                        transition:all 0.2s;
                    ">取消</button>
                    </div>
                </div>
            `;
            document.body.appendChild(importDialog);
        }

        // 创建导出密码选择对话框
        let exportPasswordDialog = document.getElementById('export-password-dialog');
        if (!exportPasswordDialog) {
            exportPasswordDialog = document.createElement('div');
            exportPasswordDialog.id = 'export-password-dialog';
            exportPasswordDialog.style.display = 'none';
            exportPasswordDialog.style.position = 'fixed';
            exportPasswordDialog.style.top = '0';
            exportPasswordDialog.style.left = '0';
            exportPasswordDialog.style.width = '100vw';
            exportPasswordDialog.style.height = '100vh';
            exportPasswordDialog.style.background = 'rgba(0,0,0,0.4)';
            exportPasswordDialog.style.zIndex = '100001';
            exportPasswordDialog.innerHTML = `
                <div style="
                    background:#fff;
                    max-width:450px;
                    margin:20vh auto;
                    padding:25px;
                    border-radius:10px;
                    box-shadow:0 8px 30px rgba(0,0,0,0.3);
                    border:1px solid #e0e0e0;
                ">
                    <div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;">
                    <span style="font-size:20px;">&#128228;</span>
                    <h3 style="margin:0;color:#333;font-size:18px;">导出配置</h3>
                    </div>
                    <p style="margin:0 0 15px 0;color:#666;font-size:14px;">请选择导出时的密码处理方式:</p>
                    <div style="margin-bottom:20px;">
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="export-option-current" name="export-password-option" value="current" checked>
                        <label for="export-option-current" style="margin-left:8px;">使用当前主密码(保持加密状态)</label>
                    </div>
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="export-option-new" name="export-password-option" value="new">
                        <label for="export-option-new" style="margin-left:8px;">使用新密码重新加密</label>
                    </div>
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="export-option-none" name="export-password-option" value="none">
                        <label for="export-option-none" style="margin-left:8px;">无密码导出(明文)</label>
                    </div>
                    </div>
                    <div id="new-password-input" style="display:none;margin-bottom:15px;">
                    <label style="display:block;margin-bottom:5px;color:#555;">新密码:</label>
                    <input type="password" id="new-password-field" placeholder="输入新的主密码" style="
                        width:100%;
                        padding:8px;
                        border:1px solid #ddd;
                        border-radius:4px;
                        box-sizing:border-box;
                    ">
                    <div style="font-size:12px;color:#666;margin-top:5px;">
                        &#128161; 新密码将用于加密导出的配置数据
                    </div>
                    </div>
                    <div style="display:flex;gap:10px;justify-content:flex-end;">
                    <button id="confirm-export-option" style="
                        background:linear-gradient(to right,#2ecc71,#27ae60);
                        color:white;
                        border:none;
                        padding:8px 16px;
                        border-radius:4px;
                        cursor:pointer;
                        font-weight:600;
                    ">确定</button>
                    <button id="cancel-export-option" style="
                        background:linear-gradient(to right,#e74c3c,#c0392b);
                        color:white;
                        border:none;
                        padding:8px 16px;
                        border-radius:4px;
                        cursor:pointer;
                        font-weight:600;
                    ">取消</button>
                    </div>
                </div>
            `;
            document.body.appendChild(exportPasswordDialog);
        }

        // 创建密码输入对话框
        let passwordDialog = document.getElementById('password-input-dialog');
        if (!passwordDialog) {
            passwordDialog = document.createElement('div');
            passwordDialog.id = 'password-input-dialog';
            passwordDialog.style.display = 'none';
            passwordDialog.style.position = 'fixed';
            passwordDialog.style.top = '0';
            passwordDialog.style.left = '0';
            passwordDialog.style.width = '100vw';
            passwordDialog.style.height = '100vh';
            passwordDialog.style.background = 'rgba(0,0,0,0.4)';
            passwordDialog.style.zIndex = '100001';
            passwordDialog.innerHTML = `
                <div style="
                    background:#fff;
                    max-width:400px;
                    margin:20vh auto;
                    padding:25px;
                    border-radius:10px;
                    box-shadow:0 8px 30px rgba(0,0,0,0.3);
                    border:1px solid #e0e0e0;
                ">
                    <div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;">
                    <span style="font-size:20px;">&#128272;</span>
                    <h3 style="margin:0;color:#333;font-size:18px;">密码解密失败</h3>
                    </div>
                    <p style="margin:0 0 15px 0;color:#666;font-size:14px;">检测到加密的密码无法解密,可能是主密码不同。请选择处理方式:</p>
                    <div style="margin-bottom:20px;">
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="option-keep-encrypted" name="password-option" value="keep" checked>
                        <label for="option-keep-encrypted" style="margin-left:8px;">保持加密状态(需要手动输入密码)</label>
                    </div>
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="option-clear-password" name="password-option" value="clear">
                        <label for="option-clear-password" style="margin-left:8px;">清空密码字段</label>
                    </div>
                    <div style="margin-bottom:10px;">
                        <input type="radio" id="option-manual-input" name="password-option" value="manual">
                        <label for="option-manual-input" style="margin-left:8px;">手动输入主密码</label>
                    </div>
                    </div>
                    <div id="manual-password-input" style="display:none;margin-bottom:15px;">
                    <label style="display:block;margin-bottom:5px;color:#555;">手动输入密码:</label>
                    <input type="password" id="manual-password-field" placeholder="输入密码" style="
                        width:100%;
                        padding:8px;
                        border:1px solid #ddd;
                        border-radius:4px;
                        box-sizing:border-box;
                    ">
                    </div>
                    <div style="display:flex;gap:10px;justify-content:flex-end;">
                    <button id="confirm-password-option" style="
                        background:linear-gradient(to right,#2ecc71,#27ae60);
                        color:white;
                        border:none;
                        padding:8px 16px;
                        border-radius:4px;
                        cursor:pointer;
                        font-weight:600;
                    ">确定</button>
                    <button id="cancel-password-option" style="
                        background:linear-gradient(to right,#e74c3c,#c0392b);
                        color:white;
                        border:none;
                        padding:8px 16px;
                        border-radius:4px;
                        cursor:pointer;
                        font-weight:600;
                    ">取消</button>
                    </div>
                </div>
            `;
            document.body.appendChild(passwordDialog);
        }

        // 导出全部
        document.getElementById('export-all-btn').addEventListener('click', function () {
            // 显示导出密码选择对话框
            showExportPasswordDialog();
        });

        // 导入弹窗
        document.getElementById('import-all-btn').addEventListener('click', function () {
            importDialog.style.display = 'block';
            const textarea = document.getElementById('import-textarea');
            textarea.value = '';
            textarea.focus();
        });

        // 确认导入
        importDialog.addEventListener('click', async function (e) {
            if (e.target.id === 'cancel-import') {
                importDialog.style.display = 'none';
            }
            if (e.target.id === 'confirm-import') {
                const val = document.getElementById('import-textarea').value;
                if (!val) return Utils.showError(CONSTANTS.MESSAGES.errors.importRequired);
                try {
                    const arr = JSON.parse(val);
                    if (!Array.isArray(arr)) throw new Error(CONSTANTS.MESSAGES.errors.importFormat);

                    // 处理导入的配置
                    await processImportConfigs(arr, EventBinder);

                    importDialog.style.display = 'none';
                } catch (e) {
                    Utils.showError(CONSTANTS.MESSAGES.errors.importFailed + e.message);
                }
            }
        });

        // 点击遮罩关闭
        importDialog.addEventListener('click', function (e) {
            if (e.target === importDialog) importDialog.style.display = 'none';
        });

        // 密码选项对话框事件处理
        passwordDialog.addEventListener('click', function (e) {
            // 取消按钮
            if (e.target.id === 'cancel-password-option') {
                console.log('&#10060; 用户取消密码选项');
                passwordDialog.style.display = 'none';
                window.pendingImportConfigs = null;
                window.currentPasswordOption = null;
                return;
            }

            // 确认按钮
            if (e.target.id === 'confirm-password-option') {
                const selectedOption = document.querySelector('input[name="password-option"]:checked');
                if (!selectedOption) {
                    console.log('&#10060; 未选择密码选项');
                    return;
                }

                const manualPassword = document.getElementById('manual-password-field').value;

                // 处理密码选项
                window.currentPasswordOption = {
                    option: selectedOption.value,
                    manualPassword: manualPassword
                };

                console.log('&#128295; 密码选项:', window.currentPasswordOption);
                passwordDialog.style.display = 'none';

                // 继续处理导入
                if (window.pendingImportConfigs) {
                    console.log('&#128260; 继续处理导入配置...');
                    processImportConfigs(window.pendingImportConfigs, EventBinder);
                    window.pendingImportConfigs = null;
                } else {
                    console.log('&#10060; 没有待处理的导入配置');
                }
                return;
            }

            // 点击遮罩关闭
            if (e.target === passwordDialog) {
                // console.log('&#10060; 用户点击遮罩关闭');
                passwordDialog.style.display = 'none';
                window.pendingImportConfigs = null;
                window.currentPasswordOption = null;
            }
        });

        // 密码选项单选按钮事件
        passwordDialog.addEventListener('change', function (e) {
            if (e.target.name === 'password-option') {
                console.log('&#128260; 密码选项变更:', e.target.value);
                const manualInput = document.getElementById('manual-password-input');
                if (e.target.value === 'manual') {
                    manualInput.style.display = 'block';
                } else {
                    manualInput.style.display = 'none';
                }
            }
        });

        // 导出密码选择对话框事件
        exportPasswordDialog.addEventListener('click', function (e) {
            // 取消按钮
            if (e.target.id === 'cancel-export-option') {
                exportPasswordDialog.style.display = 'none';
                return;
            }
            // 确认按钮
            if (e.target.id === 'confirm-export-option') {
                const selectedOption = document.querySelector('input[name="export-password-option"]:checked');
                if (!selectedOption) {
                    console.log('&#10060; 未选择导出选项');
                    return;
                }

                const newPassword = document.getElementById('new-password-field').value;

                // 验证新密码
                if (selectedOption.value === 'new' && !newPassword.trim()) {
                    console.log('&#10060; 新密码为空');
                    Utils.showError(CONSTANTS.MESSAGES.errors.newPasswordRequired);
                    return;
                }

                // 处理导出选项
                const exportOption = {
                    option: selectedOption.value,
                    newPassword: newPassword.trim()
                };

                console.log('&#128295; 导出选项:', exportOption);
                exportPasswordDialog.style.display = 'none';

                // 执行导出
                processExportConfigs(exportOption);
                return;
            }

            // 点击遮罩关闭
            if (e.target === exportPasswordDialog) {
                console.log('&#10060; 用户点击遮罩关闭');
                exportPasswordDialog.style.display = 'none';
            }
        });

        // 导出密码选项单选按钮事件
        exportPasswordDialog.addEventListener('change', function (e) {
            if (e.target.name === 'export-password-option') {
                console.log('&#128260; 导出选项变更:', e.target.value);
                const newPasswordInput = document.getElementById('new-password-input');
                if (e.target.value === 'new') {
                    newPasswordInput.style.display = 'block';
                } else {
                    newPasswordInput.style.display = 'none';
                }
            }
        });
    }

    /**
     * 显示导出密码选择对话框
     */
    function showExportPasswordDialog() {
        // console.log('&#128228; 显示导出密码选择对话框');
        const dialog = document.getElementById('export-password-dialog');
        if (dialog) {
            // 重置选项
            document.getElementById('export-option-current').checked = true;
            document.getElementById('new-password-field').value = '';
            document.getElementById('new-password-input').style.display = 'none';
            dialog.style.display = 'block';
        }
    }

    /**
     * 处理导出配置
     * @param {Object} exportOption 导出选项
     */
    async function processExportConfigs(exportOption) {
        // console.log('&#128260; 开始处理导出配置...');
        // console.log('&#128295; 导出选项:', exportOption);

        try {
            // 获取所有配置
            const allConfigs = DataManager.getAllConfigs().map(c => ({ ...c.data }));
            console.log('&#128202; 获取到配置数量:', allConfigs.length);

            // 根据选项处理密码
            for (const cfg of allConfigs) {
                if (cfg && cfg.password && Utils.isPasswordEncrypted(cfg.password)) {
                    console.log('&#128272; 处理加密密码:', cfg.currentUrl);

                    switch (exportOption.option) {
                        case 'current':
                            console.log('&#128274; 保持当前加密状态');
                            // 保持当前加密状态,不做任何处理
                            break;

                        case 'new':
                            console.log('&#128260; 使用新密码重新加密');
                            if (exportOption.newPassword) {
                                try {
                                    // 先用当前主密码解密
                                    const decryptedPassword = await Utils.decryptPassword(cfg.password, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                                    // 然后用新密码重新加密
                                    cfg.password = await Utils.encryptPassword(decryptedPassword, exportOption.newPassword);
                                    console.log('&#9989; 重新加密成功');
                                } catch (error) {
                                    console.warn('&#9888;&#65039; 重新加密失败:', error);
                                    // 加密失败时保持原样
                                }
                            } else {
                                console.log('&#10060; 新密码为空,保持原样');
                            }
                            break;

                        case 'none':
                            console.log('&#128275; 解密为明文');
                            try {
                                // 解密为明文
                                cfg.password = await Utils.decryptPassword(cfg.password, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                                console.log('&#9989; 解密成功');
                            } catch (error) {
                                console.warn('&#9888;&#65039; 解密失败:', error);
                                // 解密失败时保持原样
                            }
                            break;
                    }
                } else {
                    console.log('&#128221; 密码无需处理(明文或为空):', cfg.currentUrl);
                }
            }

            // 导出为JSON
            const json = JSON.stringify(allConfigs, null, 2);
            GM_setClipboard(json);

            // 显示成功消息
            let successMsg = CONSTANTS.MESSAGES.success.exportSuccess;
            if (exportOption.option === 'new') {
                successMsg += '(使用新密码重新加密)';
            } else if (exportOption.option === 'none') {
                successMsg += '(明文导出)';
            }

            Utils.showMsg(successMsg);
            console.log('&#9989; 导出完成');

        } catch (error) {
            console.error('&#10060; 导出失败:', error);
            Utils.showError(CONSTANTS.MESSAGES.errors.exportFailed, error);
        }
    }

    /**
     * 处理导入的配置
     * @param {Array} configs 配置数组
     * @param {Object} EventBinder 事件绑定器对象
     */
    async function processImportConfigs(configs, EventBinder) {
        let count = 0;
        let hasEncryptedPassword = false;
        let hasDecryptFailedPassword = false;

        // 检查是否有加密的密码
        // console.log('&#128269; 开始检查导入配置中的密码...');

        // 如果已经有密码选项,说明用户已经选择了处理方式,跳过解密检查
        if (window.currentPasswordOption) {
            // console.log('&#128295; 检测到已有密码选项,跳过解密检查');
            // console.log('&#128221; 密码选项:', window.currentPasswordOption);
            hasEncryptedPassword = false;
            hasDecryptFailedPassword = false;
        } else {
            // 首次检查,尝试解密
            for (const cfg of configs) {
                if (cfg && cfg.password && Utils.isPasswordEncrypted(cfg.password)) {
                    // console.log('&#128272; 发现加密密码:', cfg.currentUrl);
                    hasEncryptedPassword = true;
                    // 尝试解密
                    try {
                        const decryptedResult = await Utils.decryptPassword(cfg.password, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                        console.log('&#9989; 密码解密成功:', cfg.currentUrl, '解密结果长度:', decryptedResult.length);
                        // 检查解密结果是否还是加密状态
                        if (decryptedResult.startsWith('ENCRYPTED:')) {
                            console.log('&#9888;&#65039; 解密结果仍然是加密状态,可能是解密失败:', cfg.currentUrl);
                            hasDecryptFailedPassword = true;
                            break;
                        }
                    } catch (error) {
                        console.log('&#10060; 密码解密失败:', cfg.currentUrl, error.message);
                        hasDecryptFailedPassword = true;
                        break;
                    }
                }
            }
        }

        // console.log('&#128202; 检查结果:', {
        //  hasEncryptedPassword,
        //  hasDecryptFailedPassword,
        //  totalConfigs: configs.length
        // });

        // 如果有无法解密的密码,显示密码选项对话框
        if (hasEncryptedPassword && hasDecryptFailedPassword) {
            // console.log('&#128680; 检测到无法解密的密码,显示密码选项对话框');
            window.pendingImportConfigs = configs;
            document.getElementById('password-input-dialog').style.display = 'block';
            return;
        } else {
            // console.log('&#9989; 所有密码都可以正常解密,直接导入');
        }

        // 处理配置导入
        // console.log('&#128260; 开始处理配置导入...');
        for (const cfg of configs) {
            if (cfg && cfg.currentUrl) {
                // console.log('&#128221; 处理配置:', cfg.currentUrl);

                // 处理密码
                if (cfg.password && Utils.isPasswordEncrypted(cfg.password)) {
                    // console.log('&#128272; 发现加密密码,应用密码选项');
                    const passwordOption = window.currentPasswordOption || { option: 'keep' };
                    // console.log('&#128295; 密码选项:', passwordOption);

                    switch (passwordOption.option) {
                        case 'clear':
                            // console.log('&#128465;&#65039; 清空密码');
                            cfg.password = '';
                            break;
                        case 'manual':
                            // console.log('&#9999;&#65039; 使用手动输入的主密码解密');
                            const manualMasterPassword = passwordOption.manualPassword || '';
                            if (manualMasterPassword && Utils.isPasswordEncrypted(cfg.password)) {
                                try {
                                    // 使用手动输入的主密码解密原始密码
                                    const decryptedPassword = await Utils.decryptPassword(cfg.password, manualMasterPassword);
                                    // 然后用当前主密码重新加密
                                    cfg.password = await Utils.encryptPassword(decryptedPassword, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                                    // console.log('&#128272; 使用手动主密码解密成功,已重新加密');
                                } catch (error) {
                                    console.warn('&#9888;&#65039; 手动主密码解密失败:', error);
                                    // 解密失败时保持原样
                                    console.log('&#128274; 保持原始加密状态');
                                }
                            } else {
                                console.log('&#10060; 手动主密码为空或原始密码未加密');
                                cfg.password = '';
                            }
                            break;
                        case 'keep':
                        default:
                            // console.log('&#128274; 保持加密状态');
                            // 保持加密状态
                            break;
                    }
                } else {
                    // console.log('&#128221; 密码无需处理(明文或为空)');
                }

                // 保存配置
                // console.log('&#128190; 保存配置...');
                await DataManager.saveConfig(cfg, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                count++;
                console.log('&#9989; 配置保存成功');
            }
        }

        // 清理临时变量
        window.currentPasswordOption = null;

        // 更新界面
        EventBinder.allConfigs = DataManager.getAllConfigs();
        EventBinder.currentKey = null;
        UIRenderer.renderConfigList(EventBinder.allConfigs, null, document.getElementById('config-search').value);
        UIRenderer.loadConfigToForm({}).then(() => {
            // 导入后自动展开选择器区域
            selectorsCollapsed = false;
            const section = document.getElementById('selectors-section');
            const btn = document.getElementById('toggle-selectors-btn');
            const listArea = document.querySelector('.config-list-area');
            if (section && btn) {
                section.style.display = '';
                btn.textContent = '▼';
                // 同步调整左侧列表高度
                if (listArea) {
                    listArea.style.height = CONSTANTS.PANEL_DIMENSIONS.height.expanded;
                }
            }
            Utils.showMsg(CONSTANTS.MESSAGES.success.importSuccess + count + CONSTANTS.MESSAGES.success.configs);
        });
    }

    // ========== 自动登录/自动填充功能 ========== 
    /**
     * 执行自动登录或自动填充
     */
    async function doAutoLoginOrFill() {
        // 获取当前URL(不含查询参数和hash)
        const currentUrl = window.location.origin + window.location.pathname;
        let configKey = DataManager.CONFIG_PREFIX + currentUrl;
        let config = DataManager.getConfig(configKey);

        // 检查配置是否有效
        if (!config) {
            console.log('自动登录:未找到精确匹配的配置,尝试域名匹配');
        }

        // 没有精确匹配,尝试域名匹配
        if (!config || (!config.autoLogin && !config.autoFill)) {
            const domain = window.location.hostname;
            const all = DataManager.getAllConfigs();
            for (const c of all) {
                const cfg = c.data;
                // 检查配置是否有效
                if (!cfg || typeof cfg !== 'object') {
                    console.warn('自动登录:跳过无效配置', c.key);
                    continue;
                }

                if ((cfg.autoLogin || cfg.autoFill) && cfg.currentUrl) {
                    try {
                        const urlObj = new URL(cfg.currentUrl);
                        if (urlObj.hostname === domain) {
                            config = cfg;
                            break;
                        }
                    } catch (e) { }
                }
            }
        }

        // 最终检查配置是否有效
        if (!config || typeof config !== 'object') {
            console.log('自动登录:未找到有效的配置');
            return;
        }

        if (config && (config.autoLogin || config.autoFill)) {
            // 解密密码
            let decryptedPassword = config.password;
            if (Utils.isPasswordEncrypted(config.password)) {
                try {
                    decryptedPassword = await Utils.decryptPassword(config.password, CONSTANTS.ENCRYPTION.MASTER_PASSWORD);
                } catch (error) {
                    console.warn('自动填充时密码解密失败:', error);
                    return; // 解密失败时不执行自动填充
                }
            }

            // 执行先手点击操作
            await executePreClickActions(config);

            setTimeout(() => {
                // 支持更多类型的用户名/密码输入框
                let usernameInput = null, passwordInput = null;
                try {
                    usernameInput = document.querySelector(config.selectors?.username) ||
                        document.querySelector("input[type='text'], input[name*='user'], input[name*='account'], input[placeholder*='户名'], input[placeholder*='账号']");
                    passwordInput = document.querySelector(config.selectors?.password) ||
                        document.querySelector("input[type='password']");
                } catch (e) { }
                if (usernameInput && passwordInput) {
                    usernameInput.value = config.username;
                    passwordInput.value = decryptedPassword;
                    ['input', 'change'].forEach(eventType => {
                        const event = new Event(eventType, { bubbles: true });
                        usernameInput.dispatchEvent(event);
                        passwordInput.dispatchEvent(event);
                    });

                    // 输出自动填充完成信息
                    console.log(`&#9989; 自动填充完成 - 网站: ${config.currentUrl}, 用户: ${config.username}`);

                    // 自动聚焦到登录按钮
                    const loginButton = document.querySelector(config.selectors?.loginButton) || document.querySelector("button[type='submit'], input[type='submit'], button.login, .login-btn");
                    if (loginButton) loginButton.focus();
                    if (config.autoLogin) {
                        if (loginButton) {
                            setTimeout(() => {
                                loginButton.click();
                                console.log(`&#128640; 自动登录已触发 - 网站: ${config.currentUrl}`);
                            }, CONSTANTS.ANIMATION.autoLoginDelay);
                        }
                    }
                } else {
                    console.warn(`&#9888;&#65039; 自动填充失败 - 未找到输入框 - 网站: ${config.currentUrl}`);
                }
            }, CONSTANTS.ANIMATION.pageLoadDelay);
        }
    }
    window.addEventListener('load', doAutoLoginOrFill);

    // ========== 跳转页面按钮功能 ==========
    /**
     * 设置跳转按钮功能
     */
    function setupGotoUrlBtn() {
        const btn = document.getElementById('goto-url-btn');
        const input = document.getElementById('edit-url');
        // 防止重复绑定
        btn.onclick = null;
        btn.addEventListener('click', function () {
            let url = input.value.trim();
            if (!url) return Utils.showError(CONSTANTS.MESSAGES.errors.urlRequired);
            // 补全协议
            if (!/^https?:\/\//i.test(url)) {
                url = 'https://' + url;
            }
            try {
                // 再次校验
                const realUrl = new URL(url).href;
                window.open(realUrl, '_blank');
            } catch (e) {
                Utils.showError(CONSTANTS.MESSAGES.errors.urlInvalid);
            }
        });
    }

    // ========== 跳转按钮显示/隐藏逻辑 ==========
    /**
     * 更新跳转按钮的显示状态
     */
    function updateGotoBtnVisibility() {
        const btn = document.getElementById('goto-url-btn');
        const input = document.getElementById('edit-url');
        const currentUrl = window.location.origin + window.location.pathname;
        const configUrl = input.value.trim();

        if (!configUrl) {
            btn.style.display = 'inline-block';
            return;
        }

        // 比较URL(忽略协议、查询参数、哈希等)
        let configUrlNormalized = configUrl;
        let currentUrlNormalized = currentUrl;

        try {
            const configUrlObj = new URL(configUrl.startsWith('http') ? configUrl : 'https://' + configUrl);
            const currentUrlObj = new URL(currentUrl);

            configUrlNormalized = configUrlObj.hostname + configUrlObj.pathname;
            currentUrlNormalized = currentUrlObj.hostname + currentUrlObj.pathname;
        } catch (e) {
            // 如果URL解析失败,使用原始值比较
        }

        // 如果URL基本相同,隐藏按钮
        if (configUrlNormalized === currentUrlNormalized) {
            btn.style.display = 'none';
        } else {
            btn.style.display = 'inline-block';
        }
    }

    // ========== 拖拽功能实现 ==========
    /**
     * 设置面板拖拽功能
     * @param {Element} panel 面板元素
     */
    function setupPanelDrag(panel) {
        const header = document.getElementById('panel-header');
        let isDragging = false, startX = 0, startY = 0, origX = 0, origY = 0;
        header.style.cursor = 'move';
        header.onmousedown = function (e) {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            origX = panel.offsetLeft;
            origY = panel.offsetTop;
            document.body.style.userSelect = 'none';
            // 拖动时增加透明度
            panel.style.opacity = CONSTANTS.OPACITY.dragging;
            panel.style.transition = 'opacity ' + CONSTANTS.ANIMATION.duration + ' ease';
        };
        document.onmousemove = function (e) {
            if (!isDragging) return;
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            panel.style.left = (origX + dx) + 'px';
            panel.style.top = (origY + dy) + 'px';
            panel.style.right = '';
            panel.style.bottom = '';
            panel.style.transform = '';
        };
        document.onmouseup = function () {
            if (isDragging) {
                isDragging = false;
                document.body.style.userSelect = '';
                // 拖动结束时恢复透明度
                panel.style.opacity = CONSTANTS.OPACITY.normal;
                panel.style.transition = 'opacity ' + CONSTANTS.ANIMATION.duration + ' ease';
            }
        };
    }

    // ========== 关闭按钮和ESC关闭功能 ==========
    /**
     * 设置关闭按钮和ESC关闭功能
     * @param {Element} panel 面板元素
     */
    function setupCloseBtnAndEsc(panel) {
        const closeBtn = document.getElementById('close-panel');
        // 防止重复绑定
        closeBtn.onclick = null;
        closeBtn.addEventListener('click', function () {
            panel.style.display = 'none';
            // 重置所有临时状态
            resetPanelState();
        });
        // ESC关闭
        document.addEventListener('keydown', function (e) {
            if (panel.style.display !== 'none' && (e.key === 'Escape' || e.key === 'Esc')) {
                panel.style.display = 'none';
                // 重置所有临时状态
                resetPanelState();
            }
        });
    }

    /**
     * 重置面板状态(不删除保存的数据)
     */
    function resetPanelState() {
        // 重置选择器状态
        isPickingElement = false;
        currentPickingInput = null;
        currentPickingIsMulti = false;
        if (originalCursor) {
            document.body.style.cursor = originalCursor;
            originalCursor = null;
        }

        // 移除选择器样式
        const style = document.getElementById('element-picker-style');
        if (style) style.remove();

        // 重置UI状态
        const template = DataManager.getConfigTemplate('default');
        template.currentUrl = window.location.origin + window.location.pathname;
        template.nickname = document.title || '新配置';
        UIRenderer.loadConfigToForm(template);

        // 重置选择器区域状态
        selectorsCollapsed = true;
        const section = document.getElementById('selectors-section');
        const btn = document.getElementById('toggle-selectors-btn');
        const listArea = document.querySelector('.config-list-area');
        if (section && btn) {
            section.style.display = 'none';
            btn.textContent = '▲';
            if (listArea) {
                listArea.style.height = CONSTANTS.PANEL_DIMENSIONS.height.collapsed;
            }
        }

        // 重置当前选中配置
        EventBinder.currentKey = null;

        // 移除多选提示框
        removeMultiSelectHint();

        // 清理删除按钮事件监听器
        const deleteBtn = document.getElementById('delete-config-btn');
        if (deleteBtn) {
            const newDeleteBtn = deleteBtn.cloneNode(true);
            deleteBtn.parentNode.replaceChild(newDeleteBtn, deleteBtn);
            // 重新绑定事件
            newDeleteBtn.addEventListener('click', function () {
                if (!EventBinder.currentKey) return Utils.showError(CONSTANTS.MESSAGES.errors.configRequired);

                // 清理现有的确认框
                const existingDialog = document.getElementById('confirm-dialog');
                if (existingDialog) existingDialog.remove();

                Utils.showConfirm(CONSTANTS.MESSAGES.confirm.deleteConfig, async () => {
                    DataManager.deleteConfig(EventBinder.currentKey);
                    EventBinder.currentKey = null;
                    EventBinder.allConfigs = DataManager.getAllConfigs();
                    UIRenderer.renderConfigList(EventBinder.allConfigs, null, document.getElementById('config-search').value);
                    await UIRenderer.loadConfigToForm({});
                    Utils.showMsg(CONSTANTS.MESSAGES.success.configDeleted);
                });
            });
        }

        console.log('&#9989; 面板状态已重置');
    }

    // ========== 左侧列表隐藏/显示功能 ==========
    let listHidden = true;
    /**
     * 设置左侧列表隐藏/显示功能
     */
    function setupListToggle() {
        const toggleBtn = document.getElementById('toggle-list-btn');
        const listArea = document.getElementById('config-list-area');
        const formArea = document.querySelector('.form-area');

        // 初始化状态
        if (listHidden) {
            listArea.style.display = 'none';
            toggleBtn.textContent = '&#128203;';
            formArea.style.width = '100%';
        } else {
            listArea.style.display = 'flex';
            toggleBtn.textContent = '&#128203;';
            formArea.style.width = '';
        }

        // 移除所有现有事件监听器
        const newToggleBtn = toggleBtn.cloneNode(true);
        toggleBtn.parentNode.replaceChild(newToggleBtn, toggleBtn);

        // 重新绑定事件
        newToggleBtn.addEventListener('click', function () {
            listHidden = !listHidden;
            if (listHidden) {
                listArea.style.display = 'none';
                newToggleBtn.textContent = '&#128203;';
                formArea.style.width = '100%';
            } else {
                listArea.style.display = 'flex';
                newToggleBtn.textContent = '&#128203;';
                formArea.style.width = '';
            }
        });
    }

    // ========== 元素选择器功能 ==========
    let isPickingElement = false;
    let currentPickingInput = null;
    let currentPickingIsMulti = false;
    let originalCursor = null;

    /**
     * 设置元素选择器功能
     */
    function setupElementPicker() {
        const pickButtons = [
            { btn: 'pick-username-selector', input: 'edit-username-selector' },
            { btn: 'pick-password-selector', input: 'edit-password-selector' },
            { btn: 'pick-login-button-selector', input: 'edit-login-button-selector' },
            { btn: 'pick-pre-click-selector', input: 'edit-pre-click-selector', isMulti: true }
        ];

        pickButtons.forEach(({ btn, input, isMulti }) => {
            const pickBtn = document.getElementById(btn);
            const inputEl = document.getElementById(input);

            // 移除所有现有事件监听器
            const newPickBtn = pickBtn.cloneNode(true);
            pickBtn.parentNode.replaceChild(newPickBtn, pickBtn);

            // 重新绑定事件
            newPickBtn.addEventListener('click', function () {
                // 如果已经在选择模式中,点击按钮停止选择
                if (isPickingElement && currentPickingInput === inputEl) {
                    stopElementPicking();
                    return;
                }
                // 否则开始选择模式
                startElementPicking(inputEl, newPickBtn, isMulti);
            });
        });
    }

    /**
     * 开始元素选择模式
     * @param {Element} inputEl 输入框元素
     * @param {Element} pickBtn 选择按钮元素
     * @param {boolean} isMulti 是否支持多选
     */
    function startElementPicking(inputEl, pickBtn, isMulti = false) {
        if (isPickingElement) return;

        isPickingElement = true;
        currentPickingInput = inputEl;
        currentPickingIsMulti = isMulti;
        originalCursor = document.body.style.cursor;

        // 改变按钮状态
        pickBtn.textContent = isMulti ? '&#9209;&#65039;' : '&#9209;&#65039;';
        pickBtn.style.background = '#e74c3c';
        pickBtn.style.color = 'white';
        pickBtn.title = isMulti ? '点击停止多选模式' : '点击停止选择';

        // 为多选模式添加特殊指示
        if (isMulti) {
            pickBtn.style.border = '2px solid #ff6b6b';
            pickBtn.style.borderRadius = '4px';
        }

        // 改变页面光标
        document.body.style.cursor = 'crosshair';

        // 添加选择模式样式
        const style = document.createElement('style');
        style.id = 'element-picker-style';
        style.textContent = `
            *:hover {
                outline: 2px solid #3498db !important;
                outline-offset: 1px !important;
            }
            .element-picker-active {
                outline: 2px solid #e74c3c !important;
                outline-offset: 1px !important;
                            }
        `;
        document.head.appendChild(style);

        // 绑定页面点击事件
        document.addEventListener('click', handleElementClick, true);
        document.addEventListener('keydown', handleEnterKey);

        // 显示提示
        if (isMulti) {
            Utils.showMsg('点击页面元素进行选择(多选模式,按ESC结束选择,或点击停止按钮)', 'info');
            // 为多选模式创建固定提示框
            createMultiSelectHint();
        } else {
            Utils.showMsg('点击页面元素进行选择', 'info');
        }
    }

    /**
     * 处理元素点击事件
     * @param {Event} e 点击事件
     * @returns {boolean} 是否阻止默认行为
     */
    function handleElementClick(e) {
        if (!isPickingElement) return;

        e.preventDefault();
        e.stopPropagation();

        const element = e.target;

        // 检查是否为弹窗元素,避免选择到弹窗、模态框等
        if (Utils.isPopupElement(element)) {
            Utils.showMsg(CONSTANTS.MESSAGES.errors.popupElement, 'error');
            return false;
        }

        const selector = generateSelector(element);

        // 填入选择器
        if (currentPickingInput) {
            if (currentPickingIsMulti) {
                // 多选模式:追加到现有选择器
                const currentValue = currentPickingInput.value.trim();
                const newValue = currentValue ? `${currentValue}, ${selector}` : selector;
                currentPickingInput.value = newValue;

                // 显示提示信息
                Utils.showMsg(`已添加选择器: ${selector}(继续点击添加更多,按ESC或点击停止按钮结束)`, 'info');

                // 多选模式下不退出选择模式,允许继续选择
                return false;
            } else {
                // 单选模式:直接替换
                currentPickingInput.value = selector;
                // 退出选择模式
                stopElementPicking();
            }
        }

        return false;
    }

    /**
    * 处理回车键退出选择模式
    * @param {Event} e 键盘事件
    */
    function handleEnterKey(e) {
        if (e.key === 'Enter' && isPickingElement) {
            // 阻止事件冒泡,避免触发面板的ESC关闭功能
            e.preventDefault();
            e.stopPropagation();
            stopElementPicking();
        }
    }

    /**
     * 创建多选模式提示框
     */
    function createMultiSelectHint() {
        // 移除已存在的提示框
        removeMultiSelectHint();

        const hint = document.createElement('div');
        hint.id = 'multi-select-hint';
        hint.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #ff6b6b;
            color: white;
            padding: 12px 16px;
            border-radius: 6px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.3);
            z-index: 99999999;
            font-size: 14px;
            font-weight: 600;
            border: 2px solid #ff4757;
            animation: slideIn 0.3s ease;
        `;
        hint.innerHTML = `
            <div style="display:flex;align-items:center;gap:8px;">
                <span>&#127919;</span>
                <span>多选模式 - 点击元素添加,按ESC结束</span>
                <button onclick="document.getElementById('multi-select-hint')?.remove()" 
                style="background:none;border:none;color:white;cursor:pointer;font-size:16px;padding:0;margin-left:8px;">×</button>
            </div>
        `;
        document.body.appendChild(hint);
    }

    /**
     * 移除多选模式提示框
     */
    function removeMultiSelectHint() {
        const hint = document.getElementById('multi-select-hint');
        if (hint) {
            hint.remove();
        }
    }

    /**
     * 停止元素选择模式
     */
    function stopElementPicking() {
        if (!isPickingElement) return;

        isPickingElement = false;
        currentPickingInput = null;
        currentPickingIsMulti = false;

        // 恢复按钮状态
        const pickButtons = ['pick-username-selector', 'pick-password-selector', 'pick-login-button-selector', 'pick-pre-click-selector'];
        pickButtons.forEach(btnId => {
            const btn = document.getElementById(btnId);
            if (btn) {
                btn.textContent = '&#127919;';
                btn.style.background = '';
                btn.style.color = '';
                btn.style.border = '';
                btn.style.borderRadius = '';
                btn.title = '选择元素';
            }
        });

        // 恢复页面光标
        document.body.style.cursor = originalCursor || '';

        // 移除选择模式样式
        const style = document.getElementById('element-picker-style');
        if (style) style.remove();

        // 移除事件监听器
        document.removeEventListener('click', handleElementClick, true);
        document.removeEventListener('keydown', handleEnterKey);

        // 移除多选提示框
        removeMultiSelectHint();

        // 显示停止提示
        Utils.showMsg('元素选择模式已停止', 'success');
    }

    /**
     * 根据元素生成CSS选择器
     * @param {Element} element DOM元素
     * @returns {string} CSS选择器
     */
    function generateSelector(element) {
        // 辅助函数:检查文本是否包含关键词
        const containsKeywords = (text, keywords) => {
            return keywords.some(keyword => text.toLowerCase().includes(keyword));
        };

        // 辅助函数:从class列表中查找特定class
        const findClassByKeywords = (classes, keywords) => {
            return classes.find(c => containsKeywords(c, keywords));
        };

        // 优先使用ID
        if (element.id) {
            return `#${element.id}`;
        }

        const tagName = element.tagName.toLowerCase();

        // 根据元素类型生成特定选择器
        if (tagName === 'input') {
            // 密码输入框
            if (element.type === 'password') {
                return "input[type='password']";
            }

            // 用户名输入框
            if (element.type === 'text') {
                // 优先使用name属性
                if (element.name) {
                    if (containsKeywords(element.name, CONSTANTS.SELECTOR.userKeywords)) {
                        return `input[name="${element.name}"]`;
                    }
                }

                // 使用placeholder
                if (element.placeholder) {
                    if (containsKeywords(element.placeholder, CONSTANTS.SELECTOR.userKeywords)) {
                        return `input[placeholder*="${element.placeholder}"]`;
                    }
                }

                // 使用class
                if (element.className && typeof element.className === 'string') {
                    const classes = element.className.split(' ').filter(c => c.trim());
                    const userClass = findClassByKeywords(classes, CONSTANTS.SELECTOR.userKeywords);
                    if (userClass) {
                        return `.${userClass}`;
                    }
                }

                return "input[type='text']";
            }

            // 其他输入框类型
            if (element.type) {
                return `${tagName}[type="${element.type}"]`;
            }
        }

        // 按钮元素
        if (tagName === 'button') {
            // 提交按钮
            if (element.type === 'submit') {
                return "button[type='submit']";
            }

            // 登录按钮
            if (element.textContent) {
                if (containsKeywords(element.textContent, CONSTANTS.SELECTOR.loginKeywords)) {
                    return `button:contains("${element.textContent.trim()}")`;
                }
            }

            // 使用class
            if (element.className && typeof element.className === 'string') {
                const classes = element.className.split(' ').filter(c => c.trim());
                const loginClass = findClassByKeywords(classes, ['login', 'submit', 'btn']);
                if (loginClass) {
                    return `.${loginClass}`;
                }
            }
        }

        // 输入框元素
        if (tagName === 'input') {
            // 使用name属性
            if (element.name) {
                return `${tagName}[name="${element.name}"]`;
            }

            // 使用placeholder
            if (element.placeholder) {
                return `${tagName}[placeholder*="${element.placeholder}"]`;
            }
        }

        // 使用class(选择最具体的class)
        if (element.className && typeof element.className === 'string') {
            const classes = element.className.split(' ').filter(c => c.trim());
            if (classes.length > 0) {
                // 选择第一个非通用class
                const specificClass = classes.find(c =>
                    !CONSTANTS.SELECTOR.genericClasses.includes(c.toLowerCase())
                ) || classes[0];
                return `.${specificClass}`;
            }
        }

        // 使用标签名
        return tagName;
    }

    // ===================== 菜单命令与快捷键 =====================
    /**
     * 初始化配置管理面板
     * 统一处理面板的显示和所有功能的初始化
     */
    async function initializePanel() {
        // 显示管理面板
        manager.style.display = 'block';

        // 初始化事件绑定器,加载配置数据并绑定事件
        await EventBinder.init();

        // 设置选择器验证功能,包括点击验证和实时预览
        setupSelectorValidation();

        // 设置导入导出功能,支持批量导入导出配置
        setupImportExport(EventBinder);

        // 设置选择器区域为折叠状态(默认隐藏)
        selectorsCollapsed = true;

        // 设置选择器区域折叠/展开功能
        setupSelectorCollapse();

        // 设置面板拖拽功能,允许用户拖动面板位置
        setupPanelDrag(manager);

        // 设置跳转按钮功能,点击可跳转到配置的网址
        setupGotoUrlBtn();

        // 设置关闭按钮和ESC键关闭功能
        setupCloseBtnAndEsc(manager);

        // 设置密码显示/隐藏切换功能
        setupPasswordToggle();

        // 设置左侧列表隐藏/显示功能
        setupListToggle();

        // 更新跳转按钮的显示状态(根据当前页面地址智能显示/隐藏)
        updateGotoBtnVisibility();

        // 设置元素选择器功能,支持点击页面元素自动生成选择器
        setupElementPicker();
    }

    /**
     * 注册Tampermonkey菜单命令
     * 在Tampermonkey扩展的菜单中添加"打开登录配置管理工具"选项
     * 用户可以通过点击菜单来打开配置管理面板
     */
    GM_registerMenuCommand('打开登录配置管理工具', initializePanel);

    /**
     * 注册键盘快捷键监听器
     * 监听Ctrl+Shift+L组合键,快速打开配置管理面板
     * 提供比菜单更便捷的打开方式
     */
    document.addEventListener('keydown', function (e) {
        // 检测是否按下Ctrl+Shift+L组合键
        if (e.ctrlKey && e.shiftKey && e.key === 'L') {
            initializePanel();
            // 阻止默认的浏览器快捷键行为
            e.preventDefault();
        }
    });

    // ===================== 清理机制 =====================
    /**
     * 页面卸载时清理标记
     */
    window.addEventListener('beforeunload', function () {
        // 清理初始化标记
        delete window.loginManagerInitialized;
        // 清理可能的事件监听器
        if (window.loginManagerEventListeners) {
            window.loginManagerEventListeners.forEach(listener => {
                try {
                    listener.element.removeEventListener(listener.type, listener.handler, listener.options);
                } catch (e) {
                    // 忽略清理错误
                }
            });
            delete window.loginManagerEventListeners;
        }

        console.log('&#9989; 登录配置管理工具:页面卸载,清理完成');
    });
    // 初始化事件监听器数组
    window.loginManagerEventListeners = [];
    console.log('&#9989; 登录配置管理工具:初始化完成');

})();
ybwei2008 发表于 2026-1-28 16:07
雾都孤尔 发表于 2026-1-28 16:09
经常申请账号或登录什么的用这个省事,用户信息是存在浏览器缓存里的吗?
wkdxz 发表于 2026-1-28 16:25
本帖最后由 wkdxz 于 2026-1-28 16:27 编辑

谢谢分享,已经用上了!
picoyiyi 发表于 2026-1-28 16:28
好想法啊!不然只能存浏览器里
 楼主| MRBANK 发表于 2026-1-28 16:30
wkdxz 发表于 2026-1-28 16:25
谢谢分享,这个是每个输入框都要弹出来吗?能不能批量填写?

填写完表单后点击捕捉,把需要记录的框都点一遍,填写时候是批量填写的
 楼主| MRBANK 发表于 2026-1-28 16:32
wkdxz 发表于 2026-1-28 16:25
谢谢分享,已经用上了!

求个免费的评分好不好,哈哈哈哈
Do_zh 发表于 2026-1-28 16:35
好东西。感谢分享。
yhage 发表于 2026-1-28 16:41
用来批量注册吗?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-14 13:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表