免费中国大学Mooc答题助手(HappyMooc)

🔥🔥免费好用的中国大学Mooc答题助手(无需购买题库)!

// ==UserScript==
// @name         免费中国大学Mooc答题助手(HappyMooc)
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  🔥🔥免费好用的中国大学Mooc答题助手(无需购买题库)!
// @author       Your Name
// @match        https://www.icourse163.org/learn/*
// @match        http://www.icourse163.org/learn/*
// @match        http://www.icourse163.org/spoc/learn/*
// @match        https://www.icourse163.org/spoc/learn/*
// @match        https://www.icourse163.org/mooc/*
// @grant        GM.xmlHttpRequest
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @license MIT
// ==/UserScript==
 
(function () {
    'use strict';
    const debug = false
    const API_CONFIG = {
        BASE_URL: debug?'http://localhost:5001':"https://mooc.gxx.cool",
        ENDPOINTS: {
            AID_CHANGE: '/api/aidChange',
            USER_STATUS: '/api/user/status'
        }
    };
    let qrcode = document.createElement('script');
    qrcode.src = "https://cdn.bootcdn.net/ajax/libs/qrcodejs/1.0.0/qrcode.min.js";
    document.head.appendChild(qrcode);
    const buildUrl = (endpoint, params = {}) => {
        let url = API_CONFIG.BASE_URL + endpoint;
        Object.keys(params).forEach(key => {
            url = url.replace(`{${key}}`, params[key]);
        });
        return url;
    };

    const user_info = unsafeWindow.webUser || GM_getValue('webUser');
    const global_data = { webUser: { ...user_info }, courseDto: { ...unsafeWindow.courseDto }, curseData: {} };
    console.log(global_data);

    class ToastManager {
        constructor() {
            this.container = null;
            this.toasts = [];
            this.init();
        }

        init() {
            this.addStyles();
            this.createContainer();
        }

        addStyles() {
            const style = document.createElement('style');
            style.textContent = `
                .toast-container {
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    z-index: 10000;
                    pointer-events: none;
                }

                .toast {
                    background: #fff;
                    border-radius: 8px;
                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
                    margin-bottom: 10px;
                    padding: 16px 20px;
                    min-width: 300px;
                    max-width: 400px;
                    pointer-events: auto;
                    transform: translateX(100%);
                    transition: all 0.3s ease;
                    border-left: 4px solid #007bff;
                    display: flex;
                    align-items: center;
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    font-size: 14px;
                    line-height: 1.4;
                }

                .toast.show {
                    transform: translateX(0);
                }

                .toast.success {
                    border-left-color: #28a745;
                }

                .toast.error {
                    border-left-color: #dc3545;
                }

                .toast.warning {
                    border-left-color: #ffc107;
                }

                .toast.info {
                    border-left-color: #17a2b8;
                }

                .toast-icon {
                    margin-right: 12px;
                    font-size: 18px;
                    flex-shrink: 0;
                }

                .toast.success .toast-icon::before {
                    content: '✓';
                    color: #28a745;
                }

                .toast.error .toast-icon::before {
                    content: '✕';
                    color: #dc3545;
                }

                .toast.warning .toast-icon::before {
                    content: '⚠';
                    color: #ffc107;
                }

                .toast.info .toast-icon::before {
                    content: 'ℹ';
                    color: #17a2b8;
                }

                .toast-content {
                    flex: 1;
                }

                .toast-title {
                    font-weight: 600;
                    margin-bottom: 4px;
                    color: #333;
                }

                .toast-message {
                    color: #666;
                }

                .toast-close {
                    margin-left: 12px;
                    background: none;
                    border: none;
                    font-size: 18px;
                    color: #999;
                    cursor: pointer;
                    padding: 0;
                    width: 20px;
                    height: 20px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 50%;
                    transition: all 0.2s ease;
                }

                .toast-close:hover {
                    background: #f0f0f0;
                    color: #666;
                }

                .modal-overlay {
                    position: fixed;
                    top: 0;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    background: rgba(0, 0, 0, 0.5);
                    z-index: 10001;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    opacity: 0;
                    transition: opacity 0.3s ease;
                }

                .modal-overlay.show {
                    opacity: 1;
                }

                .modal {
                    background: #fff;
                    border-radius: 12px;
                    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
                    max-width: 500px;
                    width: 90%;
                    max-height: 80vh;
                    overflow: hidden;
                    transform: scale(0.9);
                    transition: transform 0.3s ease;
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                }

                .modal-overlay.show .modal {
                    transform: scale(1);
                }

                .modal-header {
                    padding: 20px 24px 16px;
                    border-bottom: 1px solid #eee;
                    display: flex;
                    align-items: center;
                    justify-content: space-between;
                }

                .modal-title {
                    font-size: 18px;
                    font-weight: 600;
                    color: #333;
                    margin: 0;
                }

                .modal-close {
                    background: none;
                    border: none;
                    font-size: 24px;
                    color: #999;
                    cursor: pointer;
                    padding: 0;
                    width: 32px;
                    height: 32px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 50%;
                    transition: all 0.2s ease;
                }

                .modal-close:hover {
                    background: #f0f0f0;
                    color: #666;
                }

                .modal-body {
                    padding: 20px 24px;
                    color: #666;
                    line-height: 1.5;
                }

                .modal-footer {
                    padding: 16px 24px 20px;
                    border-top: 1px solid #eee;
                    display: flex;
                    justify-content: flex-end;
                    gap: 12px;
                }

                .btn {
                    padding: 8px 16px;
                    border-radius: 6px;
                    border: 1px solid #ddd;
                    background: #fff;
                    color: #333;
                    cursor: pointer;
                    font-size: 14px;
                    font-weight: 500;
                    transition: all 0.2s ease;
                }

                .btn:hover {
                    background: #f8f9fa;
                }

                .btn-primary {
                    background: #007bff;
                    border-color: #007bff;
                    color: #fff;
                }

                .btn-primary:hover {
                    background: #0056b3;
                    border-color: #0056b3;
                }

                .btn-danger {
                    background: #dc3545;
                    border-color: #dc3545;
                    color: #fff;
                }

                .btn-danger:hover {
                    background: #c82333;
                    border-color: #c82333;
                }
            `;
            document.head.appendChild(style);
        }

        createContainer() {
            this.container = document.createElement('div');
            this.container.className = 'toast-container';
            document.body.appendChild(this.container);
        }

        show(type, title, message, duration = 4000) {
            const toast = document.createElement('div');
            toast.className = `toast ${type}`;

            toast.innerHTML = `
                <div class="toast-icon"></div>
                <div class="toast-content">
                    <div class="toast-title">${title}</div>
                    <div class="toast-message">${message}</div>
                </div>
                <button class="toast-close">×</button>
            `;

            this.container.appendChild(toast);
            this.toasts.push(toast);

            setTimeout(() => toast.classList.add('show'), 10);

            const closeBtn = toast.querySelector('.toast-close');
            closeBtn.addEventListener('click', () => this.remove(toast));

            if (duration > 0) {
                setTimeout(() => this.remove(toast), duration);
            }

            return toast;
        }

        remove(toast) {
            toast.classList.remove('show');
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
                const index = this.toasts.indexOf(toast);
                if (index > -1) {
                    this.toasts.splice(index, 1);
                }
            }, 300);
        }

        success(title, message, duration) {
            return this.show('success', title, message, duration);
        }

        error(title, message, duration) {
            return this.show('error', title, message, duration);
        }

        warning(title, message, duration) {
            return this.show('warning', title, message, duration);
        }

        info(title, message, duration) {
            return this.show('info', title, message, duration);
        }

        confirm(title, message, onConfirm, onCancel) {
            const overlay = document.createElement('div');
            overlay.className = 'modal-overlay';

            overlay.innerHTML = `
                <div class="modal">
                    <div class="modal-header">
                        <h3 class="modal-title">${title}</h3>
                        <button class="modal-close">×</button>
                    </div>
                    <div class="modal-body">${message}</div>
                    <div class="modal-footer">
                        <button class="btn btn-cancel">取消</button>
                        <button class="btn btn-primary btn-confirm">确认</button>
                    </div>
                </div>
            `;

            document.body.appendChild(overlay);

            setTimeout(() => overlay.classList.add('show'), 10);

            const closeModal = () => {
                overlay.classList.remove('show');
                setTimeout(() => {
                    if (overlay.parentNode) {
                        overlay.parentNode.removeChild(overlay);
                    }
                }, 300);
            };

            overlay.querySelector('.modal-close').addEventListener('click', () => {
                closeModal();
                if (onCancel) onCancel();
            });

            overlay.querySelector('.btn-cancel').addEventListener('click', () => {
                closeModal();
                if (onCancel) onCancel();
            });

            overlay.querySelector('.btn-confirm').addEventListener('click', () => {
                closeModal();
                if (onConfirm) onConfirm();
            });

            overlay.addEventListener('click', (e) => {
                if (e.target === overlay) {
                    closeModal();
                    if (onCancel) onCancel();
                }
            });
        }
    }

    const base64Encode = (str) => {
        return btoa(unescape(encodeURIComponent(str)));
    };

    const toast = new ToastManager();

    const createUI = () => {
        const sidePanel = document.createElement('div');
        Object.assign(sidePanel.style, {
            position: 'fixed',
            top: '20%',
            right: '20px',
            width: '220px',
            backgroundColor: 'white',
            border: '1px solid #e1e5e9',
            borderRadius: '12px',
            boxShadow: '0 8px 32px rgba(0, 0, 0, 0.12)',
            zIndex: '10000',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
            overflow: 'hidden'
        });

        const tabContainer = document.createElement('div');
        Object.assign(tabContainer.style, {
            height: '60px',
            backgroundColor: '#f8f9fa',
            borderBottom: '1px solid #e9ecef',
            display: 'flex',
            alignItems: 'stretch'
        });

        const tab1 = document.createElement('div');
        Object.assign(tab1.style, {
            flex: '1',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500',
            color: '#007bff',
            backgroundColor: 'white',
            borderBottom: '2px solid #007bff',
            transition: 'all 0.3s ease'
        });
        tab1.textContent = '小程序码';
        tab1.setAttribute('data-tab', 'miniprogram');

        const tab2 = document.createElement('div');
        Object.assign(tab2.style, {
            flex: '1',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500',
            color: '#666',
            backgroundColor: '#f8f9fa',
            borderBottom: '2px solid transparent',
            transition: 'all 0.3s ease'
        });
        tab2.textContent = '绑定页面';
        tab2.setAttribute('data-tab', 'binding');

        const contentArea = document.createElement('div');
        Object.assign(contentArea.style, {
            padding: '16px',
            backgroundColor: 'white',
            minHeight: '200px'
        });

        const miniprogramContent = document.createElement('div');
        Object.assign(miniprogramContent.style, {
            textAlign: 'center'
        });

        const qrDescription = document.createElement('div');
        Object.assign(qrDescription.style, {
            fontSize: '12px',
            color: '#666',
            marginBottom: '16px',
            lineHeight: '1.4'
        });
        qrDescription.textContent = '扫码进入小程序使用更多功能';
        
        const miniprogramQRContainer = document.createElement('div');
        Object.assign(miniprogramQRContainer.style, {
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#f8f9fa',
            borderRadius: '8px',
            border: '1px solid #e9ecef'
        });
        
        const qrImage = document.createElement('img');
        Object.assign(qrImage.style, {
            width: '150px',
            height: '150px',
            borderRadius: '8px',
            backgroundColor: 'white',
            objectFit: 'contain'
        });
        qrImage.src = 'https://gxx.cool/adnexa/suncode.png';
        qrImage.alt = '小程序码';
        
        qrImage.onerror = function() {
            const placeholder = document.createElement('div');
            Object.assign(placeholder.style, {
                width: '200px',
                height: '200px',
                backgroundColor: '#e9ecef',
                borderRadius: '8px',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                color: '#999',
                fontSize: '12px',
                textAlign: 'center',
                flexDirection: 'column'
            });
            placeholder.innerHTML = '<div>小程序码加载失败</div><div style="font-size: 10px; margin-top: 8px; color: #ccc;">请检查网络连接</div>';
            miniprogramQRContainer.replaceChild(placeholder, qrImage);
        };
        
        miniprogramQRContainer.appendChild(qrImage);
        miniprogramContent.appendChild(qrDescription);
        miniprogramContent.appendChild(miniprogramQRContainer);

        const bindingContent = document.createElement('div');
        Object.assign(bindingContent.style, {
            display: 'none'
        });
        
        const bindingDescription = document.createElement('div');
        Object.assign(bindingDescription.style, {
            fontSize: '12px',
            color: '#666',
            textAlign: 'center',
            marginBottom: '16px',
            lineHeight: '1.4'
        });
        bindingDescription.textContent = '小程序扫描二维码完成绑定';
        
        const bindingQRContainer = document.createElement('div');
        Object.assign(bindingQRContainer.style, {
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#f8f9fa',
            borderRadius: '8px',
            border: '1px solid #e9ecef'
        });
        
        const encodedUserId = base64Encode(global_data.webUser.id);
        console.log('生成的Base64编码userid:', encodedUserId);
        
        const qrElement = document.createElement('div');
        qrElement.style.display = 'inline-block';
        
        const loadingHint = document.createElement('div');
        Object.assign(loadingHint.style, {
            padding: '30px',
            fontSize: '12px',
            color: '#666',
            textAlign: 'center'
        });
        loadingHint.textContent = '正在生成二维码...';
        qrElement.appendChild(loadingHint);
        
        setTimeout(() => {
            generateQRCode(qrElement, encodedUserId);
        }, 3000);
        bindingQRContainer.appendChild(qrElement);
        
        bindingContent.appendChild(bindingDescription);
        bindingContent.appendChild(bindingQRContainer);

        const switchTab = (activeTab) => {
            [tab1, tab2].forEach(tab => {
                const isActive = tab.getAttribute('data-tab') === activeTab;
                Object.assign(tab.style, {
                    color: isActive ? '#007bff' : '#666',
                    backgroundColor: isActive ? 'white' : '#f8f9fa',
                    borderBottomColor: isActive ? '#007bff' : 'transparent'
                });
            });
            
            miniprogramContent.style.display = activeTab === 'miniprogram' ? 'block' : 'none';
            bindingContent.style.display = activeTab === 'binding' ? 'block' : 'none';
        };
        
        tab1.addEventListener('click', (e) => {
            e.stopPropagation();
            switchTab('miniprogram');
        });
        tab2.addEventListener('click', (e) => {
            e.stopPropagation();
            switchTab('binding');
        });
        
        tabContainer.appendChild(tab1);
        tabContainer.appendChild(tab2);
        
        contentArea.appendChild(miniprogramContent);
        contentArea.appendChild(bindingContent);
        
        sidePanel.appendChild(tabContainer);
        sidePanel.appendChild(contentArea);
        
        document.body.appendChild(sidePanel);

        addDragFunctionality(sidePanel);
    };

    const createBindingUI = () => {
        const sidePanel = document.createElement('div');
        Object.assign(sidePanel.style, {
            position: 'fixed',
            top: '40%',
            right: '20px',
            width: '320px',
            backgroundColor: 'white',
            border: '1px solid #e1e5e9',
            borderRadius: '12px',
            boxShadow: '0 8px 32px rgba(0, 0, 0, 0.12)',
            zIndex: '10000',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
            overflow: 'hidden'
        });

        const tabContainer = document.createElement('div');
        Object.assign(tabContainer.style, {
            height: '60px',
            backgroundColor: '#f8f9fa',
            borderBottom: '1px solid #e9ecef',
            display: 'flex',
            alignItems: 'stretch'
        });

        const tab1 = document.createElement('div');
        Object.assign(tab1.style, {
            flex: '1',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500',
            color: '#666',
            backgroundColor: '#f8f9fa',
            borderBottom: '2px solid transparent',
            transition: 'all 0.3s ease'
        });
        tab1.textContent = '小程序码';
        tab1.setAttribute('data-tab', 'miniprogram');

        const tab2 = document.createElement('div');
        Object.assign(tab2.style, {
            flex: '1',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500',
            color: '#007bff',
            backgroundColor: 'white',
            borderBottom: '2px solid #007bff',
            transition: 'all 0.3s ease'
        });
        tab2.textContent = '绑定页面';
        tab2.setAttribute('data-tab', 'binding');

        const contentArea = document.createElement('div');
        Object.assign(contentArea.style, {
            padding: '16px',
            backgroundColor: 'white',
            minHeight: '200px'
        });

        const miniprogramContent = document.createElement('div');
        Object.assign(miniprogramContent.style, {
            textAlign: 'center',
            display: 'none'
        });
        
        const qrTitle = document.createElement('div');
        Object.assign(qrTitle.style, {
            fontSize: '16px',
            fontWeight: '600',
            color: '#333',
            marginBottom: '12px'
        });
        qrTitle.textContent = '小程序码';
        
        const qrDescription = document.createElement('div');
        Object.assign(qrDescription.style, {
            fontSize: '12px',
            color: '#666',
            marginBottom: '16px',
            lineHeight: '1.4'
        });
        qrDescription.textContent = '扫码进入小程序使用更多功能';
        
        const miniprogramQRContainer = document.createElement('div');
        Object.assign(miniprogramQRContainer.style, {
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            padding: '20px',
            backgroundColor: '#f8f9fa',
            borderRadius: '8px',
            border: '1px solid #e9ecef'
        });
        
        const qrImage = document.createElement('img');
        Object.assign(qrImage.style, {
            width: '150px',
            height: '150px',
            borderRadius: '8px',
            backgroundColor: 'white',
            border: '1px solid #ddd',
            objectFit: 'contain'
        });
        qrImage.src = 'https://gxx.cool/adnexa/suncode.png';
        qrImage.alt = '小程序码';
        
        qrImage.onerror = () => {
            qrImage.style.display = 'none';
            const errorPlaceholder = document.createElement('div');
            Object.assign(errorPlaceholder.style, {
                width: '150px',
                height: '150px',
                backgroundColor: '#f8f9fa',
                borderRadius: '8px',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                color: '#999',
                fontSize: '12px',
                textAlign: 'center',
                border: '1px solid #e9ecef'
            });
            errorPlaceholder.textContent = '图片加载失败';
            qrImage.parentNode.insertBefore(errorPlaceholder, qrImage.nextSibling);
        };
        
        miniprogramQRContainer.appendChild(qrImage);
        miniprogramContent.appendChild(qrTitle);
        miniprogramContent.appendChild(qrDescription);
        miniprogramContent.appendChild(miniprogramQRContainer);

        const bindingContent = document.createElement('div');
        
        const bindingDescription = document.createElement('div');
        Object.assign(bindingDescription.style, {
            fontSize: '12px',
            color: '#666',
            textAlign: 'center',
            marginBottom: '16px',
            lineHeight: '1.4'
        });
        bindingDescription.textContent = '请使用微信小程序扫描下方二维码完成账户绑定';
        
        const bindingQRContainer = document.createElement('div');
        Object.assign(bindingQRContainer.style, {
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            marginBottom: '16px',
            padding: '12px',
            backgroundColor: '#f8f9fa',
            borderRadius: '8px',
            border: '1px solid #e9ecef'
        });
        
        const encodedUserId = base64Encode(global_data.webUser.id);
        console.log('生成的Base64编码userid:', encodedUserId);
        
        const qrElement = document.createElement('div');
        qrElement.style.display = 'inline-block';
        
        const loadingHint = document.createElement('div');
        Object.assign(loadingHint.style, {
            padding: '30px',
            fontSize: '12px',
            color: '#666',
            textAlign: 'center'
        });
        loadingHint.textContent = '正在生成二维码...';
        qrElement.appendChild(loadingHint);
        
        generateQRCode(qrElement, encodedUserId);
        bindingQRContainer.appendChild(qrElement);
        
        const debugInfo = document.createElement('details');
        Object.assign(debugInfo.style, {
            marginTop: '12px',
            fontSize: '11px',
            color: '#999'
        });
        debugInfo.innerHTML = `
            <summary style="cursor: pointer; color: #666; margin-bottom: 6px;">显示二维码内容</summary>
            <div style="font-family: monospace; word-break: break-all; background: #f1f1f1; padding: 6px; border-radius: 4px; margin-top: 4px;">${encodedUserId}</div>
        `;
        
        bindingContent.appendChild(bindingTitle);
        bindingContent.appendChild(bindingDescription);
        bindingContent.appendChild(bindingQRContainer);
        bindingContent.appendChild(debugInfo);

        const switchTab = (activeTab) => {
            [tab1, tab2].forEach(tab => {
                const isActive = tab.getAttribute('data-tab') === activeTab;
                Object.assign(tab.style, {
                    color: isActive ? '#007bff' : '#666',
                    backgroundColor: isActive ? 'white' : '#f8f9fa',
                    borderBottomColor: isActive ? '#007bff' : 'transparent'
                });
            });
            
            miniprogramContent.style.display = activeTab === 'miniprogram' ? 'block' : 'none';
            bindingContent.style.display = activeTab === 'binding' ? 'block' : 'none';
        };
        
        tab1.addEventListener('click', (e) => {
            e.stopPropagation();
            switchTab('miniprogram');
        });
        tab2.addEventListener('click', (e) => {
            e.stopPropagation();
            switchTab('binding');
        });
        
        tabContainer.appendChild(tab1);
        tabContainer.appendChild(tab2);
        
        contentArea.appendChild(miniprogramContent);
        contentArea.appendChild(bindingContent);
        
        sidePanel.appendChild(tabContainer);
        sidePanel.appendChild(contentArea);
        
        document.body.appendChild(sidePanel);

        addDragFunctionality(sidePanel);
    };

    const generateQRCode = async (container, text) => {
        try {
            if (typeof QRCode === 'undefined') {
                throw new Error('QRCode 库未加载,请确保已正确引入 qrcodejs');
            }
            
            console.log('检测到 QRCode 库,类型:', typeof QRCode);
            
            container.innerHTML = '';
            
            const qrContainer = document.createElement('div');
            qrContainer.style.display = 'inline-block';
            qrContainer.style.padding = '10px';
            qrContainer.style.backgroundColor = '#ffffff';
            qrContainer.style.borderRadius = '8px';
            
            const qr = new QRCode(qrContainer, {
                text: text,
                width: 150,
                height: 150,
                colorDark: '#000000',
                colorLight: '#ffffff',
                correctLevel: QRCode.CorrectLevel.M
            });
            
            console.log('使用 qrcodejs 生成二维码成功');
            
            container.appendChild(qrContainer);
            
        } catch (error) {
            console.error('二维码生成失败:', error);
            console.log('切换到备用方案');
            generateFallbackQRCode(container, text);
        }
    };

    const generateFallbackQRCode = (container, text) => {
        console.log('使用备用方案生成二维码');
        
        container.innerHTML = '';
        
        const canvas = document.createElement('canvas');
        const size = 150;
        canvas.width = size;
        canvas.height = size;
        const ctx = canvas.getContext('2d');
        
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, size, size);
        
        ctx.fillStyle = '#000000';
        ctx.fillRect(0, 0, size, 20);
        ctx.fillRect(0, 0, 20, size);
        ctx.fillRect(size-20, 0, 20, size);
        ctx.fillRect(0, size-20, size, 20);
        
        const gridSize = 16;
        const cellSize = Math.floor((size - 40) / gridSize);
        const startX = 20;
        const startY = 20;
        
        ctx.fillStyle = '#000000';
        for (let i = 0; i < gridSize; i++) {
            for (let j = 0; j < gridSize; j++) {
                const seed = text.charCodeAt((i * gridSize + j) % text.length);
                if ((seed + i + j) % 3 === 0) {
                    ctx.fillRect(
                        startX + j * cellSize,
                        startY + i * cellSize,
                        cellSize - 1,
                        cellSize - 1
                    );
                }
            }
        }
        
        const cornerSize = cellSize * 3;
        ctx.fillRect(startX, startY, cornerSize, cornerSize);
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(startX + cellSize, startY + cellSize, cellSize, cellSize);
        ctx.fillStyle = '#000000';
        ctx.fillRect(startX + (gridSize - 3) * cellSize, startY, cornerSize, cornerSize);
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(startX + (gridSize - 2) * cellSize, startY + cellSize, cellSize, cellSize);
        ctx.fillStyle = '#000000';
        ctx.fillRect(startX, startY + (gridSize - 3) * cellSize, cornerSize, cornerSize);
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(startX + cellSize, startY + (gridSize - 2) * cellSize, cellSize, cellSize);
        
        const qrContainer = document.createElement('div');
        qrContainer.style.display = 'inline-block';
        qrContainer.style.padding = '10px';
        qrContainer.style.backgroundColor = '#ffffff';
        qrContainer.style.borderRadius = '8px';
        qrContainer.style.border = '1px solid #ddd';
        qrContainer.appendChild(canvas);
        
        container.appendChild(qrContainer);
        
        const hint = document.createElement('div');
        hint.style.marginTop = '10px';
        hint.style.fontSize = '12px';
        hint.style.color = '#999';
        hint.innerHTML = '请使用微信扫一扫 <span style="color: #dc3545;">⚠️ 备用显示</span>';
        container.appendChild(hint);
    };

    const checkUserStatus = () => {
        return new Promise((resolve) => {
            if (!global_data.webUser?.id) {
                console.log('用户未登录,无法检查状态');
                return resolve(false);
            }

            GM.xmlHttpRequest({
                method: 'GET',
                url: `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.USER_STATUS}?userid=${global_data.webUser.id}`,
                headers: { 'Content-Type': 'application/json' },
                timeout: 3000,
                onload: (response) => {
                    console.log(response);
                    
                    const isEnabled = handleStatusResponse(response);
                    resolve(isEnabled);
                },
                onerror: (error) => {
                    console.error('检查用户状态网络错误:', error);
                    resolve(true);
                }
            });
        });
    };

    const handleStatusResponse = (response) => {
        if (response.status !== 200) {
            console.error('检查用户状态请求失败:', response.status);
            return false;
        }

        try {
            const result = JSON.parse(response.responseText);

            if (result.status !== 0) {
                console.error('检查用户状态失败:', result.msg);
                return false;
            }

            const userData = result.data;
            console.log('用户状态检查结果:', userData);

            global_data.webUser.is_banned = userData.is_banned;
            global_data.webUser.is_bound = userData.is_bound;

            if (userData.is_banned) {
                console.log('用户已被封禁,禁止使用脚本');
                return false;
            }

            console.log('用户状态正常,允许使用脚本');
            return true;

        } catch (error) {
            console.error('解析用户状态响应失败:', error);
            return false;
        }
    };

    const reportAidChange = () => {
        GM.xmlHttpRequest({
            method: 'POST',
            url: buildUrl(API_CONFIG.ENDPOINTS.AID_CHANGE),
            headers: { 'Content-Type': 'application/json' },
            data: JSON.stringify({
                userID: global_data.webUser.id,
                aid: global_data.curseData.aid,
                tid: global_data.curseData.tid,
                courseName: global_data.courseDto.name,
                testName: global_data.curseData.tname,
                isExam: global_data.curseData.isExam
            }),
            onload: (response) => {
                if (response.status === 200) {
                    console.log('AID change reported successfully:', response.responseText);
                    toast.success('记录成功', '已同步打开试卷信息');
                } else {
                    console.error('Failed to report AID change:', response);
                    toast.error('记录失败', '');
                }
            },
            onerror: (error) => {
                console.error('Error reporting AID change:', error);
                toast.error('网络错误', '');
            }
        });
    };

    const hookXHR = () => {
        const originalXHR = unsafeWindow.XMLHttpRequest;
        unsafeWindow.XMLHttpRequest = class extends originalXHR {
            open(method, url, ...rest) {
                this._intercept = url.includes('getOpenQuizPaperDto')||url.includes('getOpenHomeworkPaperDto');
                super.open(method, url, ...rest);
            }

            set onreadystatechange(callback) {
                super.onreadystatechange = () => {
                    if (this._intercept && this.readyState === 4) {
                        try {
                            const jsonResponse = JSON.parse(this.responseText);
                            if (jsonResponse.result && jsonResponse.result.aid) {
                                global_data.curseData.aid = jsonResponse.result.aid;
                                global_data.curseData.tid = jsonResponse.result.tid;
                                global_data.curseData.tname = jsonResponse.result.tname;
                                
                                global_data.curseData.isExam = !!jsonResponse.result.examId && jsonResponse.result.examId !== -1;

                                console.log('提取到的数据:', {
                                    aid: global_data.curseData.aid,
                                    tid: global_data.curseData.tid,
                                    courseName: global_data.courseDto.name,
                                    testName: global_data.curseData.tname,
                                    examId: jsonResponse.result.examId,
                                    isExam: global_data.curseData.isExam
                                });

                                reportAidChange();
                            }
                        } catch (error) {
                            console.error('Failed to parse response as JSON:', error);
                        }
                    }
                    callback?.call(this);
                };
            }
        };
    };

    const addDragFunctionality = (element) => {
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;
        let xOffset = 0;
        let yOffset = 0;

        const dragHandle = element.children[0];
        if (!dragHandle) return;

        const rect = element.getBoundingClientRect();
        xOffset = rect.left;
        yOffset = rect.top;

        dragHandle.style.cursor = 'move';
        dragHandle.style.userSelect = 'none';

        dragHandle.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            e.stopPropagation();
            
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;

            if (e.target === dragHandle || dragHandle.contains(e.target)) {
                isDragging = true;
                element.style.transition = 'none';
                element.style.left = xOffset + 'px';
                element.style.top = yOffset + 'px';
                element.style.right = 'auto';
            }
        }

        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;

                xOffset = currentX;
                yOffset = currentY;

                const windowWidth = window.innerWidth;
                const windowHeight = window.innerHeight;
                const elementWidth = element.offsetWidth;
                const elementHeight = element.offsetHeight;

                if (currentX < 0) currentX = 0;
                if (currentX > windowWidth - elementWidth) currentX = windowWidth - elementWidth;

                if (currentY < 0) currentY = 0;
                if (currentY > windowHeight - elementHeight) currentY = windowHeight - elementHeight;

                element.style.left = currentX + 'px';
                element.style.top = currentY + 'px';
                element.style.right = 'auto';
            }
        }

        function dragEnd(e) {
            if (isDragging) {
                initialX = currentX;
                initialY = currentY;
                isDragging = false;
                element.style.transition = 'all 0.2s ease';
            }
        }
    };

    let aid = null;
    let tid = null;

    checkUserStatus().then(isEnabled => {
        if (isEnabled) {
            createUI();
            hookXHR();
            GM_setValue('webUser', global_data.webUser);
            setTimeout(() => {
                toast.success('脚本已加载', '慕课助手已成功加载!', 3000);
            }, 1000);
        } else {
            if (global_data.webUser && global_data.webUser.is_banned) {
                console.log('用户已被封禁,脚本不会加载');
                toast.error('账户被禁用', '您的账户已被禁用,无法使用此功能。如有疑问请联系管理员。', 0);
            } else if (global_data.webUser && global_data.webUser.is_bound === false) {
                console.log('用户未绑定,显示绑定二维码');
                createBindingUI();
                toast.info('需要绑定', '请扫描二维码完成账户绑定', 0);
            } else {
                console.log('用户状态异常,脚本不会加载');
                toast.error('状态异常', '用户状态异常,请检查登录状态。', 0);
            }
        }
    });
})();