Greasy Fork

Greasy Fork is available in English.

深圳大学体育场馆自动抢票

深圳大学体育场馆自动预约脚本 - iOS、移动端完全兼容

当前为 2025-06-12 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         深圳大学体育场馆自动抢票
// @namespace    http://tampermonkey.net/

// @version      1.1.0
// @description  深圳大学体育场馆自动预约脚本 - iOS、移动端完全兼容
// @author       zskfree
// @match        https://ehall.szu.edu.cn/qljfwapp/sys/lwSzuCgyy/*
// @icon         🎾
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 更精确的设备检测
    const userAgent = navigator.userAgent;
    const isMobile = /iPhone|iPad|iPod|Android|Mobile/i.test(userAgent);
    const isIOS = /iPhone|iPad|iPod/i.test(userAgent);
    const isIPad = /iPad/i.test(userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
    const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;

    console.log('设备检测:', { isMobile, isIOS, isIPad, isTouchDevice });

    // 增强的存储方案 - 兼容iOS限制
    const Storage = {
        set: function (key, value) {
            const fullKey = 'szu_sports_' + key;
            try {
                // 尝试 localStorage
                localStorage.setItem(fullKey, JSON.stringify(value));
                return true;
            } catch (e) {
                console.warn('localStorage 失败,尝试 sessionStorage:', e);
                try {
                    // 回退到 sessionStorage
                    sessionStorage.setItem(fullKey, JSON.stringify(value));
                    return true;
                } catch (e2) {
                    console.warn('sessionStorage 也失败,使用内存存储:', e2);
                    // 最后回退到内存存储
                    if (!window.memoryStorage) window.memoryStorage = {};
                    window.memoryStorage[fullKey] = value;
                    return true;
                }
            }
        },
        get: function (key, defaultValue) {
            const fullKey = 'szu_sports_' + key;
            try {
                // 尝试 localStorage
                const item = localStorage.getItem(fullKey);
                if (item !== null) {
                    return JSON.parse(item);
                }
            } catch (e) {
                console.warn('读取 localStorage 失败:', e);
            }

            try {
                // 尝试 sessionStorage
                const item = sessionStorage.getItem(fullKey);
                if (item !== null) {
                    return JSON.parse(item);
                }
            } catch (e) {
                console.warn('读取 sessionStorage 失败:', e);
            }

            // 尝试内存存储
            if (window.memoryStorage && window.memoryStorage[fullKey] !== undefined) {
                return window.memoryStorage[fullKey];
            }

            return defaultValue;
        }
    };

    // 运动项目映射
    const SPORT_CODES = {
        "羽毛球": "001",
        "排球": "003",
        "网球": "004",
        "篮球": "005",
        "游泳": "009",
        "乒乓球": "013",
        "桌球": "016"
    };

    // 校区映射
    const CAMPUS_CODES = {
        "粤海": "1",
        "丽湖": "2"
    };

    // 时间段选项
    const TIME_SLOTS = [
        "08:00-09:00", "09:00-10:00", "10:00-11:00", "11:00-12:00",
        "12:00-13:00", "13:00-14:00", "14:00-15:00", "15:00-16:00",
        "16:00-17:00", "17:00-18:00", "18:00-19:00", "19:00-20:00",
        "20:00-21:00", "21:00-22:00"
    ];

    // 场馆代码映射
    const VENUE_CODES = {
        "至畅": "104",
        "至快": "111"
    };

    // 默认配置
    const DEFAULT_CONFIG = {
        USER_INFO: {
            YYRGH: "2300123999",
            YYRXM: "张三"
        },
        TARGET_DATE: getTomorrowDate(),
        SPORT: "羽毛球",
        CAMPUS: "丽湖",
        PREFERRED_VENUE: "至畅", // 新增:优先场馆选择
        PREFERRED_TIMES: ["20:00-21:00", "21:00-22:00"],
        RETRY_INTERVAL: 1,
        MAX_RETRY_TIMES: 200,
        REQUEST_TIMEOUT: 10,
        YYLX: "1.0"
    };

    // 获取明天日期
    function getTomorrowDate() {
        const tomorrow = new Date();
        tomorrow.setDate(tomorrow.getDate() + 1);
        return tomorrow.toISOString().split('T')[0];
    }

    // 修改保存和加载配置函数
    function saveConfig(config) {
        Storage.set('bookingConfig', config);
    }

    function loadConfig() {
        try {
            const saved = Storage.get('bookingConfig', null);
            return saved ? { ...DEFAULT_CONFIG, ...saved } : DEFAULT_CONFIG;
        } catch (e) {
            return DEFAULT_CONFIG;
        }
    }

    function savePanelState(isVisible) {
        Storage.set('panelVisible', isVisible);
    }

    function loadPanelState() {
        return Storage.get('panelVisible', true);
    }

    // 全局变量
    let CONFIG = loadConfig();
    let isRunning = false;
    let retryCount = 0;
    let startTime = null;
    let successfulBookings = [];
    let controlPanel = null;
    let floatingButton = null;
    let isPanelVisible = loadPanelState();

    // 获取动态最大预约数量
    function getMaxBookings() {
        const selectedTimeSlots = CONFIG.PREFERRED_TIMES.length;
        return Math.min(selectedTimeSlots, 2); // 最多2个,但不超过选择的时间段数量
    }

    // 修改创建浮动按钮函数 - 完全重写触摸事件处理
    function createFloatingButton() {
        const button = document.createElement('div');
        button.id = 'floating-toggle-btn';

        // iOS设备尺寸优化
        const buttonSize = isIPad ? '80px' : (isMobile ? '70px' : '60px');
        const fontSize = isIPad ? '32px' : (isMobile ? '28px' : '24px');

        button.style.cssText = `
        position: fixed;
        top: ${isMobile ? '20px' : '20px'};
        right: ${isMobile ? '20px' : '20px'};
        width: ${buttonSize};
        height: ${buttonSize};
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        z-index: 10001;
        box-shadow: 0 4px 15px rgba(0,0,0,0.3);
        transition: all 0.3s ease;
        border: 3px solid rgba(255,255,255,0.2);
        font-size: ${fontSize};
        user-select: none;
        -webkit-user-select: none;
        -webkit-touch-callout: none;
        -webkit-tap-highlight-color: transparent;
        touch-action: manipulation;
    `;

        button.innerHTML = '🎾';
        button.title = '显示/隐藏抢票面板';

        // 统一的点击处理函数
        function handleButtonClick(e) {
            console.log('浮动按钮被点击,当前面板状态:', isPanelVisible);
            if (e) {
                e.preventDefault(); // 集中处理 preventDefault
                e.stopPropagation(); // 集中处理 stopPropagation
            }
            togglePanel();
        }

        // 为 iPad 特别优化的事件处理
        if (isTouchDevice) {
            let isPressed = false;
            let touchStartTime = 0;
            let hasMoved = false;
            let startX = 0, startY = 0;

            const pressThreshold = 800; // ms, 定义有效点击的最大时长
            const moveThreshold = 10; // pixels, 定义手指移动多少算作移动而非点击

            // 通用的按下处理逻辑
            function onInteractionStart(clientX, clientY, pointerType = 'touch') {
                console.log(`浮动按钮 ${pointerType} start`);
                isPressed = true;
                touchStartTime = Date.now();
                hasMoved = false;
                startX = clientX;
                startY = clientY;

                button.style.transform = 'scale(1.1)';
                button.style.opacity = '0.8';
            }

            // 通用的移动处理逻辑
            function onInteractionMove(clientX, clientY) {
                if (!isPressed) return;
                if (!hasMoved) {
                    if (Math.abs(clientX - startX) > moveThreshold || Math.abs(clientY - startY) > moveThreshold) {
                        hasMoved = true;
                        console.log('浮动按钮 moved');
                    }
                }
            }

            // 通用的抬起/结束处理逻辑
            function onInteractionEnd(e, interactionType = 'touch') {
                console.log(`浮动按钮 ${interactionType} end`, { isPressed, hasMoved, duration: Date.now() - touchStartTime });

                if (!isPressed) { // 如果没有按下状态,则重置并返回
                    button.style.transform = 'scale(1)';
                    button.style.opacity = '1';
                    return;
                }

                const pressDuration = Date.now() - touchStartTime;

                if (!hasMoved && pressDuration < pressThreshold) {
                    console.log('浮动按钮 - TAP detected');
                    handleButtonClick(e); // 调用统一处理函数
                }

                button.style.transform = 'scale(1)';
                button.style.opacity = '1';
                isPressed = false;
                hasMoved = false;
            }

            // 通用的取消处理逻辑
            function onInteractionCancel() {
                console.log('浮动按钮 interaction cancel');
                isPressed = false;
                hasMoved = false;
                button.style.transform = 'scale(1)';
                button.style.opacity = '1';
            }

            if (window.PointerEvent) {
                console.log('使用 Pointer 事件');
                button.addEventListener('pointerdown', (e) => {
                    if (!e.isPrimary || (e.pointerType !== 'touch' && e.pointerType !== 'pen')) return;
                    onInteractionStart(e.clientX, e.clientY, e.pointerType);
                    // 不在此处 e.preventDefault(),让滚动等默认行为可以发生,除非确定是点击
                });
                button.addEventListener('pointermove', (e) => {
                    if (!e.isPrimary || (e.pointerType !== 'touch' && e.pointerType !== 'pen')) return;
                    onInteractionMove(e.clientX, e.clientY);
                });
                button.addEventListener('pointerup', (e) => {
                    if (!e.isPrimary || (e.pointerType !== 'touch' && e.pointerType !== 'pen')) return;
                    onInteractionEnd(e, e.pointerType);
                });
                button.addEventListener('pointercancel', onInteractionCancel);
            } else {
                console.log('使用 Touch 事件');
                button.addEventListener('touchstart', (e) => {
                    if (e.touches.length > 1) return; // 忽略多点触控
                    const touch = e.touches[0];
                    onInteractionStart(touch.clientX, touch.clientY, 'touch');
                }, { passive: true }); // passive:true 允许默认滚动行为

                button.addEventListener('touchmove', (e) => {
                    if (!isPressed || e.touches.length > 1) return;
                    const touch = e.touches[0];
                    onInteractionMove(touch.clientX, touch.clientY);
                }, { passive: true }); // passive:true 允许默认滚动行为

                button.addEventListener('touchend', (e) => {
                    // touchend 在 e.touches 中没有信息, 使用 e.changedTouches
                    if (e.changedTouches.length > 1) return; // 通常是单点结束
                    onInteractionEnd(e, 'touch');
                }); // touchend 不应是 passive,因为 handleButtonClick 可能调用 preventDefault

                button.addEventListener('touchcancel', onInteractionCancel);
            }
        } else {
            // 桌面端使用鼠标事件
            button.addEventListener('mouseenter', () => {
                button.style.transform = 'scale(1.1)';
                button.style.boxShadow = '0 6px 20px rgba(0,0,0,0.4)';
            });
            button.addEventListener('mouseleave', () => {
                button.style.transform = 'scale(1)';
                button.style.boxShadow = '0 4px 15px rgba(0,0,0,0.3)';
            });
            button.addEventListener('click', handleButtonClick);
        }

        document.body.appendChild(button);
        console.log('浮动按钮创建完成,当前面板状态:', isPanelVisible);
        return button;
    }


    // 修改创建控制面板函数的移动端样式部分
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.id = 'auto-booking-panel';

        // iOS设备样式优化 - 修复变换原点问题
        const mobileStyles = isMobile ? `
        width: calc(100vw - 30px);
        max-width: ${isIPad ? '500px' : '380px'};
        top: ${isIPad ? '120px' : '100px'};
        left: 50%;
        /* transform: translateX(-50%); // Initial transform will be set below */
        font-size: ${isIPad ? '18px' : '16px'};
        max-height: calc(100vh - 150px);
        -webkit-overflow-scrolling: touch;
    ` : `
        width: 400px;
        top: 20px;
        right: 90px;
        max-height: 90vh;
        /* transform: translateX(0); // Initial transform will be set below */
    `;

        panel.style.cssText = `
        position: fixed;
        ${mobileStyles}
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border-radius: 15px;
        padding: 20px;
        box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        z-index: 10000;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
        color: white;
        border: 2px solid rgba(255,255,255,0.2);
        overflow-y: auto;
        /* transition: all 0.3s ease; // Replaced with more specific transition */
        transition: opacity 0.3s ease, transform 0.3s ease; /* Specific transitions for animation */
        -webkit-user-select: none;
        user-select: none;
        -webkit-tap-highlight-color: transparent;
        /* Initial state will be set below after appending */
    `;

        // iOS输入框样式优化
        const inputBaseStyle = `
            width: 100%;
            padding: ${isIPad ? '14px' : (isMobile ? '12px' : '8px')};
            border: none;
            border-radius: 6px;
            background: rgba(255,255,255,0.95);
            color: #333;
            font-size: ${isIPad ? '18px' : (isMobile ? '16px' : '14px')};
            box-sizing: border-box;
            -webkit-appearance: none;
            appearance: none;
            outline: none;
        `;

        // iOS按钮样式优化
        const buttonBaseStyle = `
            width: 100%;
            padding: ${isIPad ? '18px' : (isMobile ? '15px' : '12px')};
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: ${isIPad ? '20px' : (isMobile ? '18px' : '16px')};
            font-weight: bold;
            transition: all 0.3s;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
            -webkit-appearance: none;
            appearance: none;
            outline: none;
            -webkit-tap-highlight-color: transparent;
        `;


        panel.innerHTML = `
        <div style="margin-bottom: 15px; text-align: center; position: relative;">
            <h3 style="margin: 0; font-size: ${isMobile ? '20px' : '18px'}; text-shadow: 2px 2px 4px rgba(0,0,0,0.5);">
                🎾 自动抢票助手 v1.1.0
            </h3>
            <button id="close-panel" style="
                position: absolute;
                top: -5px;
                right: -5px;
                background: rgba(255,255,255,0.2);
                border: none;
                color: white;
                width: ${isMobile ? '35px' : '30px'};
                height: ${isMobile ? '35px' : '30px'};
                border-radius: 50%;
                cursor: pointer;
                font-size: ${isMobile ? '20px' : '16px'};
                display: flex;
                align-items: center;
                justify-content: center;
                touch-action: manipulation;
            " title="隐藏面板">×</button>
            <button id="toggle-config" style="
                background: rgba(255,255,255,0.2);
                border: 1px solid rgba(255,255,255,0.3);
                color: white;
                padding: ${isMobile ? '8px 12px' : '5px 10px'};
                border-radius: 5px;
                cursor: pointer;
                margin-top: 5px;
                font-size: ${isMobile ? '14px' : '12px'};
                touch-action: manipulation;
            ">⚙️ 配置设置</button>
        </div>

        <!-- 配置区域 -->
        <div id="config-area" style="
            background: rgba(255,255,255,0.1);
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 15px;
            display: block; /* Or load from saved state */
        ">
            <!-- 用户信息 -->
            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">👤 学号/工号:</label>
                <input id="user-id" type="text" value="${CONFIG.USER_INFO.YYRGH}" style="${inputBaseStyle}">
            </div>

            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">📝 姓名:</label>
                <input id="user-name" type="text" value="${CONFIG.USER_INFO.YYRXM}" style="${inputBaseStyle}">
            </div>

            <!-- 预约设置 -->
            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">📅 预约日期:</label>
                <input id="target-date" type="date" value="${CONFIG.TARGET_DATE}" style="${inputBaseStyle}">
            </div>

            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">🏟️ 运动项目:</label>
                <select id="sport-type" style="${inputBaseStyle}">
                    ${Object.keys(SPORT_CODES).map(sport =>
            `<option value="${sport}" ${sport === CONFIG.SPORT ? 'selected' : ''}>${sport}</option>`
        ).join('')}
                </select>
            </div>

            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">🏫 校区:</label>
                <select id="campus" style="${inputBaseStyle}">
                    ${Object.keys(CAMPUS_CODES).map(campus =>
            `<option value="${campus}" ${campus === CONFIG.CAMPUS ? 'selected' : ''}>${campus}</option>`
        ).join('')}
                </select>
            </div>

            <!-- 羽毛球场馆选择 -->
            <div id="venue-selection" style="margin-bottom: 12px; display: ${CONFIG.SPORT === '羽毛球' ? 'block' : 'none'};">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">🏟️ 优先场馆:</label>
                <select id="preferred-venue" style="${inputBaseStyle}">
                    <option value="至畅" ${CONFIG.PREFERRED_VENUE === '至畅' ? 'selected' : ''}>🏆 至畅体育馆</option>
                    <option value="至快" ${CONFIG.PREFERRED_VENUE === '至快' ? 'selected' : ''}>⚡ 至快体育馆</option>
                    <option value="全部" ${CONFIG.PREFERRED_VENUE === '全部' ? 'selected' : ''}>🔄 全部场馆</option>
                </select>
                <div style="font-size: ${isMobile ? '12px' : '10px'}; color: rgba(255,255,255,0.7); margin-top: 2px;">
                    💡 选择"全部"将按至畅>至快的顺序预约
                </div>
            </div>

            <!-- 时间段选择 -->
            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">⏰ 优先时间段 (按优先级排序):</label>
                <div id="time-slots-container" style="
                    max-height: ${isMobile ? '120px' : '100px'};
                    overflow-y: auto;
                    background: rgba(255,255,255,0.1);
                    border-radius: 4px;
                    padding: 5px;
                ">
                    ${TIME_SLOTS.map(slot => `
                        <label style="display: block; font-size: ${isMobile ? '14px' : '11px'}; margin: ${isMobile ? '5px 0' : '2px 0'}; cursor: pointer;">
                            <input type="checkbox" value="${slot}"
                                ${CONFIG.PREFERRED_TIMES.includes(slot) ? 'checked' : ''}
                                style="margin-right: 5px; transform: ${isMobile ? 'scale(1.2)' : 'scale(1)'};">
                            ${slot}
                        </label>
                    `).join('')}
                </div>
            </div>

            <!-- 运行参数 -->
            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px;">
                <div>
                    <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">⏱️ 查询间隔(秒):</label>
                    <input id="retry-interval" type="number" min="1" max="60" value="${CONFIG.RETRY_INTERVAL}" style="${inputBaseStyle}">
                </div>
                <div>
                    <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">🔄 最大重试:</label>
                    <input id="max-retry" type="number" min="10" max="9999" value="${CONFIG.MAX_RETRY_TIMES}" style="${inputBaseStyle}">
                </div>
            </div>

            <div style="margin-bottom: 12px;">
                <label style="font-size: ${isMobile ? '14px' : '12px'}; display: block; margin-bottom: 3px;">⏰ 请求超时(秒):</label>
                <input id="request-timeout" type="number" min="5" max="60" value="${CONFIG.REQUEST_TIMEOUT}" style="${inputBaseStyle}">
            </div>

            <button id="save-config" style="
                ${buttonBaseStyle}
                background: linear-gradient(45deg, #4caf50, #45a049);
                color: white;
                font-size: ${isMobile ? '16px' : '14px'};
                margin-bottom: 10px;
            ">💾 保存配置</button>
        </div>

        <!-- 当前配置显示 -->
        <div style="background: rgba(255,255,255,0.1); padding: 12px; border-radius: 8px; margin-bottom: 15px;">
            <div style="font-size: ${isMobile ? '15px' : '13px'}; margin-bottom: 5px;">
                👤 <span id="display-user">${CONFIG.USER_INFO.YYRXM} (${CONFIG.USER_INFO.YYRGH})</span>
            </div>
            <div style="font-size: ${isMobile ? '15px' : '13px'}; margin-bottom: 5px;">
                📅 <span id="display-date">${CONFIG.TARGET_DATE}</span> |
                🏟️ <span id="display-sport">${CONFIG.SPORT}</span> |
                🏫 <span id="display-campus">${CONFIG.CAMPUS}</span>
            </div>
            <div id="venue-display" style="font-size: ${isMobile ? '15px' : '13px'}; margin-bottom: 5px; display: ${CONFIG.SPORT === '羽毛球' ? 'block' : 'none'};">
                🏟️ 优先场馆: <span id="display-venue">${CONFIG.PREFERRED_VENUE || '至畅'}</span>
            </div>
            <div style="font-size: ${isMobile ? '15px' : '13px'}; margin-bottom: 5px;">
                ⏰ <span id="display-times">${CONFIG.PREFERRED_TIMES.join(', ')}</span>
            </div>
            <div style="font-size: ${isMobile ? '15px' : '13px'};">
                ⚙️ 间隔:<span id="display-interval">${CONFIG.RETRY_INTERVAL}</span>s |
                重试:<span id="display-retry">${CONFIG.MAX_RETRY_TIMES}</span> |
                超时:<span id="display-timeout">${CONFIG.REQUEST_TIMEOUT}</span>s
            </div>
            <div style="font-size: ${isMobile ? '15px' : '13px'}; margin-top: 5px;">
                🎯 进度: <span id="booking-progress">0/${getMaxBookings()} 个时段</span>
            </div>
        </div>

        <!-- 控制按钮 -->
        <div style="margin-bottom: 15px;">
            <button id="start-btn" style="
                ${buttonBaseStyle}
                background: linear-gradient(45deg, #ff6b6b, #ee5a52);
                color: white;
            ">
                🚀 开始抢票
            </button>
        </div>

        <!-- 状态日志 -->
        <div id="status-area" style="
            background: rgba(0,0,0,0.2);
            padding: 10px;
            border-radius: 8px;
            font-size: ${isMobile ? '14px' : '12px'};
            max-height: ${isMobile ? '250px' : '200px'};
            overflow-y: auto;
            border: 1px solid rgba(255,255,255,0.1);
        ">
            <div style="color: #ffd700;">🔧 等待开始...</div>
        </div>

        <div style="margin-top: 15px; text-align: center; font-size: ${isMobile ? '13px' : '11px'}; opacity: 0.8;">
            ${isMobile ? '📱 触摸优化版本' : '⚡ 快捷键: Ctrl+Shift+S 开始/停止 | Ctrl+Shift+H 显示/隐藏面板'}
        </div>
    `;

        document.body.appendChild(panel);

        // 定义 transform 值,方便复用
        const transformVisibleMobile = 'translateX(-50%) translateY(0)';
        const transformHiddenMobile = 'translateX(-50%) translateY(-30px)'; // 轻微向上滑出作为隐藏状态
        const transformVisibleDesktop = 'translateX(0)';
        const transformHiddenDesktop = 'translateX(100%)'; // 从右侧滑出作为隐藏状态

        // 根据保存的状态设置面板初始可见性、透明度和位置
        if (isPanelVisible) {
            panel.style.display = 'block';
            panel.style.opacity = '1';
            if (isMobile) {
                panel.style.transform = transformVisibleMobile;
            } else {
                panel.style.transform = transformVisibleDesktop;
            }
        } else {
            panel.style.display = 'none'; // 初始隐藏
            panel.style.opacity = '0';    // 透明
            // 设置为隐藏时的 transform,这样 togglePanel 显示时可以从此状态过渡
            if (isMobile) {
                panel.style.transform = transformHiddenMobile;
            } else {
                panel.style.transform = transformHiddenDesktop;
            }
        }

        bindEventsIOS(panel); // 将 panel 作为参数传递
        return panel;
    }

    // 修改切换面板函数
    function togglePanel() {
        console.log('togglePanel 被调用,当前面板状态 (切换前):', isPanelVisible);

        isPanelVisible = !isPanelVisible;
        savePanelState(isPanelVisible);

        console.log('切换后面板状态:', isPanelVisible);

        if (controlPanel) {
            const transformVisibleMobile = 'translateX(-50%) translateY(0)';
            const transformHiddenMobile = 'translateX(-50%) translateY(-30px)';
            const transformVisibleDesktop = 'translateX(0)';
            const transformHiddenDesktop = 'translateX(100%)'; // 面板从右侧滑出

            // 确保 transition 属性在 controlPanel 上 (已在 createControlPanel 中设置)
            // controlPanel.style.transition = 'opacity 0.3s ease, transform 0.3s ease';

            if (isPanelVisible) { // 如果要显示面板
                console.log('准备显示面板');
                controlPanel.style.display = 'block'; // 必须先 block 才能应用 transform 和 opacity

                // 设置动画起始状态 (面板在隐藏位置,透明)
                // 这确保了即使面板之前是 display:none,动画也能从正确的视觉起点开始
                if (isMobile) {
                    controlPanel.style.transform = transformHiddenMobile;
                } else {
                    controlPanel.style.transform = transformHiddenDesktop;
                }
                controlPanel.style.opacity = '0';

                // 使用 setTimeout 确保浏览器渲染了起始状态,然后再开始过渡
                setTimeout(() => {
                    controlPanel.style.opacity = '1';
                    if (isMobile) {
                        controlPanel.style.transform = transformVisibleMobile;
                    } else {
                        controlPanel.style.transform = transformVisibleDesktop;
                    }
                    console.log('面板显示动画开始');
                }, 10); // 短暂延迟,让浏览器捕获起始状态

            } else { // 如果要隐藏面板
                console.log('准备隐藏面板');
                // 开始隐藏动画 (移动到隐藏位置,变透明)
                controlPanel.style.opacity = '0';
                if (isMobile) {
                    controlPanel.style.transform = transformHiddenMobile;
                } else {
                    controlPanel.style.transform = transformHiddenDesktop;
                }
                console.log('面板隐藏动画开始');

                // 等待过渡动画完成后再设置 display: none
                setTimeout(() => {
                    if (!isPanelVisible) { // 再次检查状态,防止快速切换导致问题
                        controlPanel.style.display = 'none';
                        console.log('面板已完全隐藏 (display: none)');
                    }
                }, 300); // 300ms 对应 CSS 中的 transition-duration
            }
        }

        // 更新浮动按钮样式
        if (floatingButton) {
            console.log('更新浮动按钮样式,面板可见:', isPanelVisible);
            if (isPanelVisible) {
                floatingButton.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
                floatingButton.innerHTML = '🎾';
                floatingButton.title = '隐藏抢票面板';
            } else {
                floatingButton.style.background = 'linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%)';
                floatingButton.innerHTML = '📱'; // 可以考虑用不同图标指示面板已隐藏
                floatingButton.title = '显示抢票面板';
            }
            console.log('浮动按钮样式更新完成');
        }

        console.log('面板状态切换完成:', isPanelVisible);
    }

    // 修改 iOS 事件绑定函数
    function bindEventsIOS(panelElement) { // 接受 panelElement 作为参数
        // 为所有按钮添加通用的触摸处理
        function addButtonTouchHandler(button, clickHandler) {
            if (isTouchDevice) {
                let touchStarted = false;
                let touchStartTime = 0;

                // 移除可能存在的旧事件监听器
                button.removeEventListener('click', clickHandler);

                button.addEventListener('touchstart', (e) => {
                    touchStarted = true;
                    touchStartTime = Date.now();
                    button.style.opacity = '0.7';
                    button.style.transform = 'scale(0.95)';
                    e.preventDefault();
                }, { passive: false });

                button.addEventListener('touchend', (e) => {
                    if (touchStarted && (Date.now() - touchStartTime) < 1000) {
                        e.preventDefault();
                        e.stopPropagation();

                        button.style.opacity = '1';
                        button.style.transform = 'scale(1)';

                        // 延迟执行点击处理
                        setTimeout(() => {
                            try {
                                clickHandler();
                            } catch (error) {
                                console.error('Button click handler error:', error);
                            }
                        }, 50);
                    }
                    touchStarted = false;
                }, { passive: false });

                button.addEventListener('touchcancel', () => {
                    touchStarted = false;
                    button.style.opacity = '1';
                    button.style.transform = 'scale(1)';
                }, { passive: true });

            } else {
                // 桌面端直接使用点击事件
                button.addEventListener('click', clickHandler);
            }
        }

        // 面板关闭按钮
        const closeBtn = panelElement.querySelector('#close-panel'); // 使用 panelElement.querySelector
        if (closeBtn) {
            addButtonTouchHandler(closeBtn, () => {
                togglePanel();
            });
        }

        // 配置显示/隐藏按钮
        const toggleConfigBtn = panelElement.querySelector('#toggle-config'); // 使用 panelElement.querySelector
        if (toggleConfigBtn) {
            addButtonTouchHandler(toggleConfigBtn, () => {
                const configArea = panelElement.querySelector('#config-area'); // 使用 panelElement.querySelector
                if (configArea.style.display === 'none') {
                    configArea.style.display = 'block';
                    toggleConfigBtn.textContent = '⚙️ 隐藏配置';
                } else {
                    configArea.style.display = 'none';
                    toggleConfigBtn.textContent = '⚙️ 显示配置';
                }
            });
        }

        // 运动项目变化时显示/隐藏场馆选择
        const sportTypeSelect = panelElement.querySelector('#sport-type'); // 使用 panelElement.querySelector
        if (sportTypeSelect) {
            // select 元素使用 change 事件
            sportTypeSelect.addEventListener('change', () => {
                const sportType = sportTypeSelect.value;
                const venueSelection = panelElement.querySelector('#venue-selection'); // 使用 panelElement.querySelector
                const venueDisplay = panelElement.querySelector('#venue-display'); // 使用 panelElement.querySelector

                if (sportType === '羽毛球') {
                    if (venueSelection) venueSelection.style.display = 'block';
                    if (venueDisplay) venueDisplay.style.display = 'block';
                } else {
                    if (venueSelection) venueSelection.style.display = 'none';
                    if (venueDisplay) venueDisplay.style.display = 'none';
                }
            });
        }

        // 保存配置按钮
        const saveConfigBtn = panelElement.querySelector('#save-config'); // 使用 panelElement.querySelector
        if (saveConfigBtn) {
            addButtonTouchHandler(saveConfigBtn, () => {
                try {
                    updateConfigFromUI();
                    updateDisplayConfig();
                    addLog('✅ 配置已保存', 'success');
                } catch (error) {
                    addLog('❌ 保存配置失败: ' + error.message, 'error');
                }
            });
        }

        // 开始/停止按钮
        const startBtn = panelElement.querySelector('#start-btn'); // 使用 panelElement.querySelector
        if (startBtn) {
            addButtonTouchHandler(startBtn, () => {
                try {
                    if (isRunning) {
                        stopBooking();
                    } else {
                        updateConfigFromUI();
                        if (validateConfig()) {
                            startBooking();
                        }
                    }
                } catch (error) {
                    addLog('❌ 操作失败: ' + error.message, 'error');
                }
            });
        }

        // 快捷键 - 只在非移动端添加
        if (!isMobile) {
            document.addEventListener('keydown', (e) => {
                if (e.ctrlKey && e.shiftKey) {
                    if (e.key === 'S') {
                        e.preventDefault();
                        if (isRunning) {
                            stopBooking();
                        } else {
                            updateConfigFromUI();
                            if (validateConfig()) {
                                startBooking();
                            }
                        }
                    } else if (e.key === 'H') {
                        e.preventDefault();
                        togglePanel();
                    } else if (e.key === 'C') {
                        e.preventDefault();
                        if (isPanelVisible) {
                            const toggleBtn = panelElement.querySelector('#toggle-config'); // 使用 panelElement.querySelector
                            if (toggleBtn) toggleBtn.click();
                        }
                    }
                }
            });
        }

        // iOS输入框优化
        if (isIOS) {
            const inputs = panelElement.querySelectorAll('input, select'); // 使用 panelElement.querySelectorAll
            inputs.forEach(input => {
                // 防止iOS Safari缩放
                input.addEventListener('focus', (e) => {
                    // 对于iOS设备,设置字体大小防止缩放
                    if (input.type !== 'date' && input.type !== 'number') {
                        e.target.style.fontSize = '16px';
                    }

                    // 延迟滚动到视图中
                    setTimeout(() => {
                        e.target.scrollIntoView({
                            behavior: 'smooth',
                            block: 'center'
                        });
                    }, 300);
                });

                input.addEventListener('blur', (e) => {
                    // 恢复原始字体大小
                    e.target.style.fontSize = '';
                });
            });
        }

        // checkbox 特殊处理
        const checkboxes = panelElement.querySelectorAll('input[type="checkbox"]'); // 使用 panelElement.querySelectorAll
        checkboxes.forEach(checkbox => {
            if (isTouchDevice) {
                // 为 checkbox 的父级 label 添加触摸处理
                const label = checkbox.closest('label');
                if (label) {
                    label.style.touchAction = 'manipulation';
                    label.addEventListener('touchend', (e) => {
                        // 阻止事件冒泡,让浏览器处理 checkbox 切换
                        e.stopPropagation();
                    }, { passive: true });
                }
            }
        });
    }


    // 从UI更新配置
    function updateConfigFromUI() {
        // 获取选中的时间段
        const selectedTimes = Array.from(document.querySelectorAll('#time-slots-container input[type="checkbox"]:checked'))
            .map(cb => cb.value);

        CONFIG = {
            USER_INFO: {
                YYRGH: document.getElementById('user-id').value.trim(),
                YYRXM: document.getElementById('user-name').value.trim()
            },
            TARGET_DATE: document.getElementById('target-date').value,
            SPORT: document.getElementById('sport-type').value,
            CAMPUS: document.getElementById('campus').value,
            PREFERRED_VENUE: document.getElementById('preferred-venue')?.value || '至畅', // 新增场馆选择
            PREFERRED_TIMES: selectedTimes,
            RETRY_INTERVAL: parseInt(document.getElementById('retry-interval').value),
            MAX_RETRY_TIMES: parseInt(document.getElementById('max-retry').value),
            REQUEST_TIMEOUT: parseInt(document.getElementById('request-timeout').value),
            YYLX: "1.0"
        };

        saveConfig(CONFIG);
        // 更新进度显示
        updateProgress();
    }

    // 更新显示配置
    function updateDisplayConfig() {
        document.getElementById('display-user').textContent = `${CONFIG.USER_INFO.YYRXM} (${CONFIG.USER_INFO.YYRGH})`;
        document.getElementById('display-date').textContent = CONFIG.TARGET_DATE;
        document.getElementById('display-sport').textContent = CONFIG.SPORT;
        document.getElementById('display-campus').textContent = CONFIG.CAMPUS;

        // 更新场馆显示
        const venueDisplayElement = document.getElementById('display-venue');
        if (venueDisplayElement) {
            venueDisplayElement.textContent = CONFIG.PREFERRED_VENUE || '至畅';
        }

        document.getElementById('display-times').textContent = CONFIG.PREFERRED_TIMES.join(', ');
        document.getElementById('display-interval').textContent = CONFIG.RETRY_INTERVAL;
        document.getElementById('display-retry').textContent = CONFIG.MAX_RETRY_TIMES;
        document.getElementById('display-timeout').textContent = CONFIG.REQUEST_TIMEOUT;
    }

    // 验证配置
    function validateConfig() {
        if (!CONFIG.USER_INFO.YYRGH || !CONFIG.USER_INFO.YYRXM) {
            addLog('❌ 请填写完整的用户信息', 'error');
            return false;
        }
        if (CONFIG.PREFERRED_TIMES.length === 0) {
            addLog('❌ 请至少选择一个时间段', 'error');
            return false;
        }
        if (!CONFIG.TARGET_DATE) {
            addLog('❌ 请选择预约日期', 'error');
            return false;
        }

        // 新增:验证日期不能是过去
        const targetDate = new Date(CONFIG.TARGET_DATE);
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        if (targetDate < today) {
            addLog('❌ 预约日期不能是过去的日期', 'error');
            return false;
        }

        // 新增:验证学号格式
        if (!/^\d{8,12}$/.test(CONFIG.USER_INFO.YYRGH)) {
            addLog('⚠️ 学号格式可能不正确,请检查', 'warning');
        }

        return true;
    }

    // 添加状态日志
    function addLog(message, type = 'info') {
        const statusArea = document.getElementById('status-area');
        if (!statusArea) return;

        const colors = {
            info: '#e3f2fd',
            success: '#c8e6c9',
            warning: '#fff3e0',
            error: '#ffcdd2'
        };

        const timestamp = new Date().toLocaleTimeString();
        const logEntry = document.createElement('div');
        logEntry.style.cssText = `
            color: ${colors[type]};
            margin-bottom: 3px;
            border-left: 3px solid ${colors[type]};
            padding-left: 8px;
        `;
        logEntry.innerHTML = `[${timestamp}] ${message}`;

        statusArea.appendChild(logEntry);
        statusArea.scrollTop = statusArea.scrollHeight;

        // 保持最多50条日志
        while (statusArea.children.length > 50) {
            statusArea.removeChild(statusArea.firstChild);
        }
    }

    // 更新预约进度
    function updateProgress() {
        const currentMaxBookings = getMaxBookings();
        const progressElement = document.getElementById('booking-progress');
        if (progressElement) {
            progressElement.textContent = `${successfulBookings.length}/${currentMaxBookings} 个时段`;
        }
    }

    // iOS优化的网络请求
    async function fetchWithTimeout(url, options, timeout = CONFIG.REQUEST_TIMEOUT * 1000) {
        // iOS Safari 兼容的 AbortController
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);

        try {
            // iOS Safari 兼容的 fetch 配置
            const response = await fetch(url, {
                ...options,
                signal: controller.signal,
                credentials: 'same-origin', // iOS Safari 兼容
                mode: 'cors',
                cache: 'no-cache'
            });

            clearTimeout(timeoutId);
            return response;
        } catch (error) {
            clearTimeout(timeoutId);

            if (error.name === 'AbortError') {
                throw new Error('请求超时');
            }
            throw error;
        }
    }


    // 修改获取可用时段函数,使用优化的请求
    async function getAvailableSlots() {
        try {
            const allAvailable = [];
            const sportCode = SPORT_CODES[CONFIG.SPORT];
            const campusCode = CAMPUS_CODES[CONFIG.CAMPUS];

            for (const timeSlot of CONFIG.PREFERRED_TIMES) {
                const [startTime, endTime] = timeSlot.split("-");

                const payload = new URLSearchParams({
                    XMDM: sportCode,
                    YYRQ: CONFIG.TARGET_DATE,
                    YYLX: CONFIG.YYLX,
                    KSSJ: startTime,
                    JSSJ: endTime,
                    XQDM: campusCode
                });

                // 使用优化的请求函数
                const response = await fetchWithTimeout(
                    "https://ehall.szu.edu.cn/qljfwapp/sys/lwSzuCgyy/modules/sportVenue/getOpeningRoom.do",
                    {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                            'X-Requested-With': 'XMLHttpRequest',
                            'Accept': 'application/json, text/javascript, */*; q=0.01'
                        },
                        body: payload
                    }
                );

                if (!response.ok) {
                    addLog(`❌ 请求失败: HTTP ${response.status}`, 'error');
                    continue;
                }

                const data = await response.json();

                if (data.code !== "0") {
                    addLog(`❌ 查询时段 ${timeSlot} 失败: ${data.msg || '未知错误'}`, 'error');
                    continue;
                }

                if (data.datas && data.datas.getOpeningRoom) {
                    const rooms = data.datas.getOpeningRoom.rows || [];

                    let availableCount = 0;
                    for (const room of rooms) {
                        if (!room.disabled && room.text === "可预约") {
                            const venueName = room.CDMC || '';

                            // 根据场馆选择过滤
                            if (CONFIG.SPORT === "羽毛球" && CONFIG.PREFERRED_VENUE !== "全部") {
                                if (CONFIG.PREFERRED_VENUE === "至畅" && !venueName.includes("至畅")) {
                                    continue; // 跳过非至畅场馆
                                }
                                if (CONFIG.PREFERRED_VENUE === "至快" && !venueName.includes("至快")) {
                                    continue; // 跳过非至快场馆
                                }
                            }

                            let venuePriority = 2;
                            let courtPriority = 0; // 场地优先级,数字越小优先级越高

                            // 场馆优先级判断
                            if (venueName.includes("至畅")) {
                                venuePriority = 0;  // 至畅最优先

                                // 丽湖校区至畅羽毛球场优先级设置
                                if (CONFIG.CAMPUS === "丽湖" && CONFIG.SPORT === "羽毛球") {
                                    // 匹配"5号场"或"五号场"
                                    if (venueName.includes("5号场") || venueName.includes("五号场")) {
                                        courtPriority = -2; // 5号场地最优先
                                    }
                                    // 匹配"10号场"或"十号场"
                                    else if (venueName.includes("10号场") || venueName.includes("十号场")) {
                                        courtPriority = -1; // 10号场地次优先
                                    }
                                    // 匹配"1号场"或"一号场"
                                    else if (venueName.match(/[^0-9]1号场|^1号场|一号场/)) {
                                        courtPriority = 2; // 1号场地最低优先级
                                    }
                                    // 匹配"6号场"或"六号场"
                                    else if (venueName.includes("6号场") || venueName.includes("六号场")) {
                                        courtPriority = 2; // 6号场地最低优先级
                                    }
                                    // 其他至畅场地为默认优先级 0
                                }
                            } else if (venueName.includes("至快")) {
                                venuePriority = 1;  // 至快次之
                            }

                            const slotInfo = {
                                name: `${timeSlot} - ${venueName}`,
                                wid: room.WID,
                                timeSlot: timeSlot,
                                startTime: startTime,
                                endTime: endTime,
                                venueName: venueName,
                                venueCode: room.CGBM || '',
                                priority: CONFIG.PREFERRED_TIMES.indexOf(timeSlot),
                                venuePriority: venuePriority,
                                courtPriority: courtPriority // 场地优先级
                            };

                            allAvailable.push(slotInfo);
                            availableCount++;
                        }
                    }

                    // 只在找到可预约场地时显示简化信息
                    if (availableCount > 0) {
                        addLog(`✅ ${timeSlot} 找到 ${availableCount} 个可预约场地`, 'success');
                    }
                }
            }

            // 排序逻辑:优先级数字越小越优先
            allAvailable.sort((a, b) => {
                // 首先按场地优先级排序(数字越小优先级越高)
                if (a.courtPriority !== b.courtPriority) {
                    return a.courtPriority - b.courtPriority;
                }
                // 其次按场馆优先级排序
                if (a.venuePriority !== b.venuePriority) {
                    return a.venuePriority - b.venuePriority;
                }
                // 最后按时间优先级排序
                return a.priority - b.priority;
            });

            // 🔍 简化调试信息显示
            if (allAvailable.length > 0) {
                // 只在羽毛球且有特殊优先级场地时显示详细信息
                if (CONFIG.CAMPUS === "丽湖" && CONFIG.SPORT === "羽毛球") {
                    const hasSpecialCourts = allAvailable.some(slot =>
                        slot.courtPriority === -2 || slot.courtPriority === -1
                    );

                    if (hasSpecialCourts) {
                        const topSlot = allAvailable[0];
                        let priorityText = "";
                        if (topSlot.courtPriority === -2) {
                            priorityText = " (🏆 5号场优先)";
                        } else if (topSlot.courtPriority === -1) {
                            priorityText = " (⭐ 10号场)";
                        }
                        addLog(`🎯 优选场地: ${topSlot.venueName}${priorityText}`, 'info');
                    }
                }
            }

            return allAvailable;

        } catch (error) {
            addLog(`🔥 获取时段失败: ${error.message}`, 'error');
            return [];
        }
    }

    // 预约场地
    async function bookSlot(wid, slotName) {
        try {
            const timeSlot = CONFIG.PREFERRED_TIMES.find(time => slotName.includes(time));
            if (!timeSlot) {
                addLog(`❌ 无法从 ${slotName} 中提取时间信息`, 'error');
                return false;
            }

            // 使用新的场馆代码映射
            let venueCode = "104"; // 默认值
            for (const [venueName, code] of Object.entries(VENUE_CODES)) {
                if (slotName.includes(venueName)) {
                    venueCode = code;
                    break;
                }
            }

            const [startTime, endTime] = timeSlot.split("-");
            const sportCode = SPORT_CODES[CONFIG.SPORT];
            const campusCode = CAMPUS_CODES[CONFIG.CAMPUS];

            const payload = new URLSearchParams({
                DHID: "",
                YYRGH: CONFIG.USER_INFO.YYRGH,
                CYRS: "",
                YYRXM: CONFIG.USER_INFO.YYRXM,
                CGDM: venueCode,
                CDWID: wid,
                XMDM: sportCode,
                XQWID: campusCode,
                KYYSJD: timeSlot,
                YYRQ: CONFIG.TARGET_DATE,
                YYLX: CONFIG.YYLX,
                YYKS: `${CONFIG.TARGET_DATE} ${startTime}`,
                YYJS: `${CONFIG.TARGET_DATE} ${endTime}`,
                PC_OR_PHONE: "pc"
            });

            // 使用优化的请求函数
            const response = await fetchWithTimeout(
                "https://ehall.szu.edu.cn/qljfwapp/sys/lwSzuCgyy/sportVenue/insertVenueBookingInfo.do",
                {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                        'X-Requested-With': 'XMLHttpRequest',
                        'Accept': 'application/json, text/javascript, */*; q=0.01'
                    },
                    body: payload
                }
            );

            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }

            const result = await response.json();

            if (result.code === "0" && result.msg === "成功") {
                const dhid = result.data?.DHID || "Unknown";
                addLog(`🎉 预约成功!场地:${slotName}`, 'success');
                addLog(`📋 预约单号:${dhid}`, 'success');

                successfulBookings.push({
                    timeSlot: timeSlot,
                    venueName: slotName,
                    dhid: dhid,
                    slotName: slotName
                });

                updateProgress();
                return true;
            } else {
                const errorMsg = result.msg || "未知错误";
                addLog(`❌ 预约失败:${errorMsg}`, 'error');

                if (errorMsg.includes("只能预订2次") || errorMsg.includes("超过限制")) {
                    addLog(`🎊 已达到预约上限!`, 'success');
                    return 'limit_reached';
                }

                return false;
            }

        } catch (error) {
            addLog(`💥 预约异常: ${error.message}`, 'error');
            return false;
        }
    }

    // 主抢票循环
    async function startBooking() {
        if (isRunning) return;

        isRunning = true;
        retryCount = 0;
        startTime = new Date();
        const currentMaxBookings = getMaxBookings(); // 获取当前最大预约数量

        const startBtn = document.getElementById('start-btn');
        if (startBtn) {
            startBtn.textContent = '⏹️ 停止抢票';
            startBtn.style.background = 'linear-gradient(45deg, #f44336, #d32f2f)';
        }

        addLog(`🚀 开始自动抢票!`, 'success');
        addLog(`📊 ${CONFIG.SPORT} | ${CONFIG.CAMPUS} | ${CONFIG.TARGET_DATE} | 目标: ${currentMaxBookings} 个时段`, 'info');

        // 添加场馆选择提示
        if (CONFIG.SPORT === "羽毛球") {
            if (CONFIG.PREFERRED_VENUE === "全部") {
                addLog(`🏟️ 场馆策略: 全部场馆 (至畅 > 至快)`, 'info');
            } else {
                addLog(`🏟️ 场馆策略: 仅${CONFIG.PREFERRED_VENUE}体育馆`, 'info');
            }

            // 只在丽湖至畅时显示优先级提示
            if (CONFIG.CAMPUS === "丽湖" && (CONFIG.PREFERRED_VENUE === "至畅" || CONFIG.PREFERRED_VENUE === "全部")) {
                addLog(`🎾 至畅场地优先级: 5号 > 10号 > 其他 > 1号/6号`, 'info');
            }
        }

        try {
            while (isRunning && retryCount < CONFIG.MAX_RETRY_TIMES) {
                if (successfulBookings.length >= currentMaxBookings) {
                    addLog(`🎊 恭喜!已成功预约 ${currentMaxBookings} 个时间段!`, 'success');
                    break;
                }

                retryCount++;
                // 简化查询进度显示
                if (retryCount === 1 || retryCount % 10 === 0 || retryCount <= 5) {
                    addLog(`🔍 第 ${retryCount} 次查询 (${successfulBookings.length}/${currentMaxBookings})`);
                }

                const availableSlots = await getAvailableSlots();

                if (availableSlots.length > 0) {
                    // 简化找到场地的提示
                    if (availableSlots.length <= 5) {
                        addLog(`🎉 找到 ${availableSlots.length} 个可预约时段`, 'success');
                    } else {
                        addLog(`🎉 找到 ${availableSlots.length} 个可预约时段 (显示前5个)`, 'success');
                    }

                    const bookedTimeSlots = successfulBookings.map(booking => booking.timeSlot);
                    const remainingSlots = availableSlots.filter(slot =>
                        !bookedTimeSlots.includes(slot.timeSlot)
                    );

                    if (remainingSlots.length > 0) {
                        const timeSlotGroups = {};
                        remainingSlots.forEach(slot => {
                            if (!timeSlotGroups[slot.timeSlot]) {
                                timeSlotGroups[slot.timeSlot] = [];
                            }
                            timeSlotGroups[slot.timeSlot].push(slot);
                        });

                        for (const timeSlot of CONFIG.PREFERRED_TIMES) {
                            if (successfulBookings.length >= currentMaxBookings) break;
                            if (bookedTimeSlots.includes(timeSlot)) continue;

                            if (timeSlotGroups[timeSlot]) {
                                const slotsInTime = timeSlotGroups[timeSlot];
                                // 重新排序以确保优先级正确
                                slotsInTime.sort((a, b) => {
                                    if (a.courtPriority !== b.courtPriority) {
                                        return a.courtPriority - b.courtPriority;
                                    }
                                    return a.venuePriority - b.venuePriority;
                                });

                                const firstSlot = slotsInTime[0];

                                // 简化选择场地信息显示
                                let priorityText = "";
                                if (CONFIG.CAMPUS === "丽湖" && CONFIG.SPORT === "羽毛球" && firstSlot.venueName.includes("至畅")) {
                                    if (firstSlot.courtPriority === -2) {
                                        priorityText = " 🏆";
                                    } else if (firstSlot.courtPriority === -1) {
                                        priorityText = " ⭐";
                                    } else if (firstSlot.courtPriority === 2) {
                                        priorityText = " 🔻";
                                    }
                                }

                                addLog(`🎯 预约: ${firstSlot.venueName}${priorityText}`, 'info');

                                const result = await bookSlot(firstSlot.wid, firstSlot.name);

                                if (result === true) {
                                    addLog(`✨ ${timeSlot} 预约成功!`, 'success');
                                    if (successfulBookings.length < currentMaxBookings) {
                                        await new Promise(resolve => setTimeout(resolve, 1000));
                                    }
                                } else if (result === 'limit_reached') {
                                    break;
                                } else {
                                    await new Promise(resolve => setTimeout(resolve, 500));
                                }
                            }
                        }
                    }
                } else {
                    // 简化无可用场地的提示
                    if (retryCount <= 3 || retryCount % 20 === 0) {
                        addLog(`🔍 暂无可预约场地`, 'warning');
                    }
                }

                if (successfulBookings.length < currentMaxBookings && isRunning && retryCount < CONFIG.MAX_RETRY_TIMES) {
                    // 只在前几次或间隔显示等待信息
                    if (retryCount <= 3 || retryCount % 30 === 0) {
                        addLog(`⏳ 等待 ${CONFIG.RETRY_INTERVAL} 秒后重试...`);
                    }
                    await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY_INTERVAL * 1000));
                }
            }

        } catch (error) {
            addLog(`💥 程序异常: ${error.message}`, 'error');
        } finally {
            stopBooking();
        }
    }

    // 停止抢票
    function stopBooking() {
        if (!isRunning) return; // 防止重复调用

        isRunning = false;
        const currentMaxBookings = getMaxBookings();

        const startBtn = document.getElementById('start-btn');
        if (startBtn) {
            startBtn.textContent = '🚀 开始抢票';
            startBtn.style.background = 'linear-gradient(45deg, #ff6b6b, #ee5a52)';
        }

        if (successfulBookings.length > 0) {
            addLog(`🎉 抢票结束!成功预约 ${successfulBookings.length}/${currentMaxBookings} 个时段`, 'success');
            successfulBookings.forEach((booking, index) => {
                addLog(`${index + 1}. ${booking.slotName} (${booking.dhid})`, 'success');
            });
        } else {
            addLog(`😢 很遗憾,没有成功预约到任何时段`, 'warning');
        }

        const elapsed = startTime ? Math.round((new Date() - startTime) / 1000) : 0;
        addLog(`📊 运行时间: ${elapsed}秒, 查询次数: ${retryCount}`, 'info');
    }

    // iOS兼容的初始化检查
    function checkIOSCompatibility() {
        const issues = [];

        // 检查存储可用性
        if (!Storage.set('test', 'test') || Storage.get('test') !== 'test') {
            issues.push('存储功能受限');
        }

        // 检查 fetch 支持
        if (typeof fetch === 'undefined') {
            issues.push('网络请求不支持');
        }

        // 检查触摸支持
        if (isIOS && !isTouchDevice) {
            issues.push('触摸事件检测异常');
        }

        if (issues.length > 0) {
            addLog(`⚠️ iOS兼容性问题: ${issues.join(', ')}`, 'warning');
            addLog(`💡 建议刷新页面或重启Safari`, 'info');
        } else {
            addLog(`✅ iOS兼容性检查通过`, 'success');
        }

        return issues.length === 0;
    }

    // 修改初始化函数,增加更多调试信息
    function init() {
        if (!window.location.href.includes('ehall.szu.edu.cn/qljfwapp/sys/lwSzuCgyy')) {
            console.log('URL 不匹配,退出初始化');
            return;
        }

        console.log('开始初始化...', {
            isMobile,
            isIOS,
            isIPad,
            isTouchDevice,
            userAgent: navigator.userAgent,
            platform: navigator.platform,
            maxTouchPoints: navigator.maxTouchPoints,
            hasPointerEvent: !!window.PointerEvent
        });

        // 检查 PointerEvent 支持
        if (window.PointerEvent) {
            console.log('✅ 支持 PointerEvent API');
        } else {
            console.log('❌ 不支持 PointerEvent API,使用 TouchEvent');
        }

        // iOS兼容性检查
        const isCompatible = checkIOSCompatibility();

        // 创建浮动按钮
        floatingButton = createFloatingButton();
        console.log('浮动按钮创建完成', floatingButton);

        // 创建控制面板
        controlPanel = createControlPanel();
        console.log('控制面板创建完成', controlPanel);

        updateDisplayConfig();

        const deviceInfo = isIPad ? 'iPad' : (isMobile ? '移动端' : '桌面端');
        addLog(`🎮 自动抢票助手已就绪!(${deviceInfo})`, 'success');

        if (isIOS) {
            addLog(`🍎 iOS优化版本,触摸操作已优化`, 'info');
            if (window.PointerEvent) {
                addLog(`🎯 使用 PointerEvent API`, 'info');
            } else {
                addLog(`📱 使用 TouchEvent API`, 'info');
            }
            if (!isCompatible) {
                addLog(`⚠️ 发现兼容性问题,建议检查Safari设置`, 'warning');
            }
        }

        addLog(`📝 已加载配置,可随时修改`, 'info');
        console.log('初始化完成');

        // 测试面板状态
        console.log('初始面板状态:', isPanelVisible);
    }

    // 确保页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        // DOM 已经加载完成
        setTimeout(init, 100); // 稍作延迟以确保页面元素完全就绪
    }

})();