理工学堂助手

自动化理工学堂,让学习更轻松!✨自动提交作业、导出题目和跟踪进度,一站式搞定所有任务,提高效率,让你专注学习🚀

// ==UserScript==
// @name         理工学堂助手
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  自动化理工学堂,让学习更轻松!✨自动提交作业、导出题目和跟踪进度,一站式搞定所有任务,提高效率,让你专注学习🚀
// @author       Yi
// @match        http://lgxt.wutp.com.cn/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @require      https://unpkg.com/docx@7.1.0/build/index.js
// @icon         http://lgxt.wutp.com.cn/favicon.8de18.ico
// @homepageURL https://github.com/zygame1314/LGXT-Assistant
// @supportURL https://zygame1314.github.io/LGXT-Assistant/
// @license MIT
// ==/UserScript==

(function () {
    "use strict";

    const baseURL = "http://lgxt.wutp.com.cn/api";
    const headers = {
        Accept: "*/*",
        "Content-Type": "application/x-www-form-urlencoded",
    };

    GM_addStyle(`
        #lgxt-widget {
            position: fixed;
            bottom: 0;
            right: 0;
            width: 100%;
            max-height: 80%;
            background-color: #ffffff;
            border-radius: 12px 12px 0 0;
            font-family: 'Microsoft YaHei', sans-serif;
            font-weight: bold;
            z-index: 10000;
            box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
            overflow: hidden;
            display: flex;
            flex-direction: column;
            transition: all 0.3s ease;
            animation: slideUp 0.5s ease-in-out;
        }
        #refresh-login-btn {
            background-color: #4a90e2;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.3s ease, transform 0.3s ease;
            font-weight: bold;
            font-family: 'Microsoft YaHei', sans-serif;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        #refresh-login-btn:hover {
            background-color: #357ABD;
            transform: translateY(-2px);
        }
        #refresh-login-btn:active {
            background-color: #2c6aa6;
            transform: translateY(0);
        }
        #widget-header {
            background-color: #4a90e2;
            color: #fff;
            padding: 15px;
            font-size: 18px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }
        #widget-header h3 {
            margin: 0;
            font-weight: bold;
        }
        #widget-toggle {
            transition: transform 0.3s ease;
        }
        #widget-toggle.rotated {
            transform: rotate(180deg);
        }
        #widget-content {
            max-height: 0;
            opacity: 0;
            overflow: auto;
            transition: max-height 0.5s ease, opacity 0.5s ease;
            flex: 1;
        }
        #widget-content.expanded {
            max-height: calc(100vh - 70px);
            opacity: 1;
            overflow-y: auto;
            scrollbar-color: #4a90e2 #f1f1f1;
            scrollbar-width: thin;
        }
        .arrow {
            display: inline-block;
            transition: transform 0.3s ease;
        }
        .arrow.rotated {
            transform: rotate(180deg);
        }
        #main-content {
            display: flex;
            flex-direction: column;
            height: 100%;
        }
        .panel {
            margin: 10px;
            background-color: #f8f9fa;
            border-radius: 8px;
            overflow: hidden;
            transition: max-height 0.3s ease;
        }
        .collapsible-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px 15px;
            background-color: #e9ecef;
            cursor: pointer;
            transition: background-color 0.2s ease;
        }
        .collapsible-header h4 {
            margin: 0;
            font-size: 16px;
            color: #495057;
            font-weight: bold;
            font-family: 'Microsoft YaHei', sans-serif;
        }
        .collapsible-header > div {
            display: flex;
            align-items: center;
        }
        .button-collapse {
            background: none;
            border: none;
            color: #4a90e2;
            font-size: 14px;
            cursor: pointer;
            padding: 0;
            outline: none;
            font-weight: bold;
            font-family: 'Microsoft YaHei', sans-serif;
            transition: color 0.3s ease, transform 0.2s ease;
        }
        .button-collapse:hover {
            color: #357ABD;
            transform: translateY(-2px);
        }
        .collapsible-content {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.5s ease, opacity 0.5s ease;
            opacity: 0;
        }
        .collapsible-content.expanded {
            max-height: 1000px;
            opacity: 1;
        }
        .course-item, .work-item {
            padding: 12px 15px;
            border-bottom: 1px solid #e9ecef;
            transition: background-color 0.2s ease;
        }
        .course-item > div, .work-item > div {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 5px;
            flex-wrap: wrap;
        }
        .course-item > div span, .work-item > div span {
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 70%;
            font-size: 14px;
            color: #495057;
            font-weight: bold;
        }
        .status {
            font-size: 12px;
            padding: 3px 8px;
            border-radius: 12px;
            background-color: #dc3545;
            color: white;
            transition: background-color 0.2s ease;
            font-weight: bold;
            margin-top: 5px;
        }
        .status.completed {
            background-color: #28a745;
        }
        .course-buttons {
            display: flex;
            gap: 10px;
            margin-left: auto;
        }

        .submit-100-btn, .submit-all-btn, .submit-all-courses-btn {
            background-color: #f39c12;
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 6px;
            font-size: 13px;
            cursor: pointer;
            transition: background-color 0.2s ease, transform 0.2s ease;
            outline: none;
            font-family: 'Microsoft YaHei', sans-serif;
            font-weight: bold;
            margin-top: 5px;
        }
        .submit-100-btn:hover, .submit-all-btn:hover, .submit-all-courses-btn:hover {
            background-color: #e67e22;
            transform: translateY(-2px);
        }
        .submit-100-btn:disabled, .submit-all-btn:disabled, .submit-all-courses-btn:disabled {
            background-color: #adb5bd;
            cursor: not-allowed;
        }

        .load-works-btn {
            background-color: #2ecc71;
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 6px;
            font-size: 13px;
            cursor: pointer;
            transition: background-color 0.2s ease, transform 0.2s ease;
            outline: none;
            font-family: 'Microsoft YaHei', sans-serif;
            font-weight: bold;
            margin-top: 5px;
        }
        .load-works-btn:hover {
            background-color: #27ae60;
            transform: translateY(-2px);
        }

        .export-questions-btn, .export-all-works-btn, .export-all-courses-btn {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 6px;
            font-size: 13px;
            cursor: pointer;
            transition: background-color 0.2s ease, transform 0.2s ease;
            outline: none;
            font-family: 'Microsoft YaHei', sans-serif;
            font-weight: bold;
            margin-top: 5px;
        }
        .export-questions-btn:hover, .export-all-works-btn:hover, .export-all-courses-btn:hover {
            background-color: #2980b9;
            transform: translateY(-2px);
        }
        .export-questions-btn:disabled, .export-all-works-btn:disabled, .export-all-courses-btn:disabled {
            background-color: #adb5bd;
            cursor: not-allowed;
        }

        #login-prompt {
            padding: 20px;
            text-align: center;
            color: #495057;
        }
        #login-prompt p {
            margin: 0 0 15px 0;
            font-size: 16px;
            font-weight: bold;
        }
        #login-prompt button {
            background-color: #4a90e2;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            font-size: 14px;
            cursor: pointer;
            transition: background-color 0.2s ease, transform 0.2s ease;
            font-weight: bold;
        }
        #login-prompt button:hover {
            background-color: #357ABD;
            transform: translateY(-2px);
        }
        @keyframes slideUp {
            from { opacity: 0; transform: translateY(100%); }
            to { opacity: 1; transform: translateY(0); }
        }
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-thumb {
            background: #4a90e2;
            border-radius: 4px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 4px;
        }
        #progress-bar {
            display: flex;
            margin: 10px;
            height: 20px;
        }
        .progress-block {
            flex: 1;
            margin: 0 2px;
            background-color: #e9ecef;
            border-radius: 4px;
            transition: background-color 0.3s ease;
        }
        .progress-block.success {
            background-color: #28a745;
        }
        .progress-block.failure {
            background-color: #dc3545;
        }
        #status-message {
            padding: 10px;
            color: #495057;
            background-color: #e9ecef;
            border-radius: 6px;
            margin: 10px;
            font-size: 14px;
            font-weight: bold;
        }
        @media (min-width: 768px) {
            #lgxt-widget {
                width: 450px;
                right: 20px;
                bottom: 20px;
                border-radius: 12px;
            }
            #widget-content.expanded {
                max-height: 800px;
            }
            .course-item > div span, .work-item > div span {
                max-width: 200px;
            }
        }
    `);

    const createUI = () => {
        const container = document.createElement("div");
        container.id = "lgxt-widget";
        container.innerHTML = `
            <div id="widget-header">
                <h3>理工学堂助手</h3>
                <span id="widget-toggle" class="arrow">▼</span>
            </div>
            <div id="widget-content">
                <div id="status-message" style="display:none;"></div>
                <div id="main-content">
                    <div id="course-panel" class="panel">
                        <div id="course-header" class="collapsible-header">
                            <h4>课程列表</h4>
                            <button id="collapse-course-btn" class="button-collapse">收起 ▲</button>
                        </div>
                        <div id="course-list" class="collapsible-content expanded"></div>
                        <div id="submit-all-courses-container" style="padding: 10px; text-align: center;">
                            <button id="submit-all-courses-btn" class="submit-all-courses-btn">一键提交所有课程作业100分</button>
                            <button id="export-all-courses-btn" class="export-all-courses-btn" style="margin-left: 10px;">导出所有课程作业</button>
                        </div>
                    </div>
                    <div id="work-panel" class="panel">
                        <div id="work-header" class="collapsible-header">
                            <h4>作业列表</h4>
                            <button id="collapse-work-btn" class="button-collapse">收起 ▲</button>
                        </div>
                        <div id="work-list" class="collapsible-content expanded"></div>
                        <div id="submit-all-container" style="padding: 10px; text-align: center;">
                            <button id="submit-all-btn" class="submit-all-btn">一键提交当前课程作业100分</button>
                        </div>
                    </div>
                </div>
                <div id="login-prompt" style="display:none;">
                    <p>请先登录。</p>
                    <button id="refresh-login-btn">刷新登录状态</button>
                </div>
            </div>
        `;
        document.body.appendChild(container);

        const widgetHeader = document.getElementById("widget-header");
        const widgetContent = document.getElementById("widget-content");
        const widgetToggle = document.getElementById("widget-toggle");

        widgetContent.classList.remove("expanded");

        widgetHeader.addEventListener("click", () => {
            if (widgetContent.classList.contains("expanded")) {
                widgetContent.classList.remove("expanded");
                widgetToggle.classList.remove("rotated");
            } else {
                widgetContent.classList.add("expanded");
                widgetToggle.classList.add("rotated");
            }
        });

        const toggleCollapse = (contentId, button) => {
            const content = document.getElementById(contentId);
            content.classList.toggle("expanded");
            if (content.classList.contains("expanded")) {
                button.textContent = "收起 ▲";
            } else {
                button.textContent = "展开 ▼";
            }
        };

        document.getElementById("course-header").addEventListener("click", () => {
            const button = document.getElementById("collapse-course-btn");
            toggleCollapse("course-list", button);
        });

        document
            .getElementById("collapse-course-btn")
            .addEventListener("click", (e) => {
                e.stopPropagation();
                const button = document.getElementById("collapse-course-btn");
                toggleCollapse("course-list", button);
            });

        document.getElementById("work-header").addEventListener("click", () => {
            const button = document.getElementById("collapse-work-btn");
            toggleCollapse("work-list", button);
        });

        document
            .getElementById("collapse-work-btn")
            .addEventListener("click", (e) => {
                e.stopPropagation();
                const button = document.getElementById("collapse-work-btn");
                toggleCollapse("work-list", button);
            });

        document
            .getElementById("refresh-login-btn")
            .addEventListener("click", () => {
                checkLoginStatus();
            });

        document.getElementById("submit-all-btn").addEventListener("click", () => {
            submitAllAnswers();
        });

        document.getElementById("submit-all-courses-btn").addEventListener("click", () => {
            submitAllCoursesAnswers();
        });

        document.getElementById("export-all-courses-btn").addEventListener("click", () => {
            exportAllCoursesWorks();
        });

        checkLoginStatus();
        interceptLoginResponse();
    };

    const interceptLoginResponse = () => {
        const originalXHROpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function (method, url) {
            this._url = url;
            return originalXHROpen.apply(this, arguments);
        };

        const originalXHRSend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function () {
            this.addEventListener(
                "readystatechange",
                function () {
                    if (this.readyState === 4 && this.status === 200) {
                        if (this._url.includes("/api/login")) {
                            const response = this.responseText;
                            try {
                                const result = JSON.parse(response);
                                if (result.code === 0 && result.data) {
                                    const token = result.data;
                                    GM_setValue("token", token);
                                    showNotification("登录成功,已获取凭证!", "success");
                                    checkLoginStatus();
                                }
                            } catch (e) {
                                console.error("解析登录响应出错", e);
                            }
                        }
                    }
                },
                false
            );
            return originalXHRSend.apply(this, arguments);
        };
    };

    const checkLoginStatus = () => {
        const myCoursesUrl = `${baseURL}/myCourses`;

        const requestHeaders = {
            ...headers,
        };
        const token = GM_getValue("token", "");
        if (token) {
            requestHeaders.Authorization = token;
        } else {
            document.getElementById("main-content").style.display = "none";
            document.getElementById("login-prompt").style.display = "block";
            return;
        }

        GM_xmlhttpRequest({
            method: "POST",
            url: myCoursesUrl,
            headers: requestHeaders,
            onload: (response) => {
                const result = JSON.parse(response.responseText);
                if (result.code === 0) {
                    document.getElementById("main-content").style.display = "block";
                    document.getElementById("login-prompt").style.display = "none";
                    getMyCourses();
                } else {
                    showNotification("登录失效,请重新登录!", "error");
                    GM_setValue("token", "");
                    document.getElementById("main-content").style.display = "none";
                    document.getElementById("login-prompt").style.display = "block";
                }
            },
            onerror: (error) => {
                showNotification("网络错误,请检查!", "error");
                console.error(error);
            },
        });
    };

    const getMyCourses = () => {
        const myCoursesUrl = `${baseURL}/myCourses`;

        const requestHeaders = {
            ...headers,
        };
        const token = GM_getValue("token", "");
        if (token) {
            requestHeaders.Authorization = token;
        } else {
            showNotification("未检测到登录信息,请先登录!", "error");
            document.getElementById("main-content").style.display = "none";
            document.getElementById("login-prompt").style.display = "block";
            return;
        }

        GM_xmlhttpRequest({
            method: "POST",
            url: myCoursesUrl,
            headers: requestHeaders,
            onload: (response) => {
                const result = JSON.parse(response.responseText);
                if (result.code === 0) {
                    const courses = result.data;
                    displayCourses(courses);
                } else {
                    showNotification("获取课程失败:" + result.msg, "error");
                    document.getElementById("main-content").style.display = "none";
                    document.getElementById("login-prompt").style.display = "block";
                }
            },
            onerror: (error) => {
                showNotification("网络错误,请检查!", "error");
                console.error(error);
            },
        });
    };

    const getMyCoursesAsync = () => {
        return new Promise((resolve, reject) => {
            const myCoursesUrl = `${baseURL}/myCourses`;

            const requestHeaders = {
                ...headers,
            };
            const token = GM_getValue("token", "");
            if (token) {
                requestHeaders.Authorization = token;
            } else {
                showNotification("未检测到登录信息,请先登录!", "error");
                document.getElementById("main-content").style.display = "none";
                document.getElementById("login-prompt").style.display = "block";
                reject(new Error("未登录"));
                return;
            }

            GM_xmlhttpRequest({
                method: "POST",
                url: myCoursesUrl,
                headers: requestHeaders,
                onload: (response) => {
                    const result = JSON.parse(response.responseText);
                    if (result.code === 0) {
                        const courses = result.data;
                        resolve(courses);
                    } else {
                        showNotification("获取课程失败:" + result.msg, "error");
                        reject(new Error("获取课程失败"));
                    }
                },
                onerror: (error) => {
                    showNotification("网络错误,请检查!", "error");
                    console.error(error);
                    reject(error);
                },
            });
        });
    };

    const displayCourses = (courses) => {
        const courseList = document.getElementById("course-list");
        courseList.innerHTML = "";

        courses.forEach((course) => {
            const courseItem = document.createElement("div");
            courseItem.className = "course-item";
            courseItem.innerHTML = `
            <div>
                <span title="${course.courseName}">${course.courseName}</span>
                <div class="course-buttons">
                    <button class="load-works-btn" data-course-id="${course.courseId}">查看作业</button>
                    <button class="export-all-works-btn" data-course-id="${course.courseId}">导出所有作业</button>
                </div>
            </div>
        `;
            courseList.appendChild(courseItem);
        });

        document.querySelectorAll(".load-works-btn").forEach((btn) => {
            btn.addEventListener("click", (e) => {
                const courseId = e.target.getAttribute("data-course-id");
                getCourseWorks(courseId);
            });
        });

        document.querySelectorAll(".export-all-works-btn").forEach((btn) => {
            btn.addEventListener("click", async (e) => {
                const courseId = e.target.getAttribute("data-course-id");
                await exportAllWorks(courseId);
            });
        });
    };

    let currentWorks = [];

    const getCourseWorks = (courseId) => {
        const myCourseWorksUrl = `${baseURL}/myCourseWorks`;
        const data = `courseId=${courseId}`;

        const requestHeaders = {
            ...headers,
        };
        const token = GM_getValue("token", "");
        if (token) {
            requestHeaders.Authorization = token;
        } else {
            showNotification("未检测到登录信息,请先登录!", "error");
            document.getElementById("main-content").style.display = "none";
            document.getElementById("login-prompt").style.display = "block";
            return;
        }

        GM_xmlhttpRequest({
            method: "POST",
            url: myCourseWorksUrl,
            headers: requestHeaders,
            data: data,
            onload: (response) => {
                const result = JSON.parse(response.responseText);
                if (result.code === 0) {
                    const works = result.data;
                    currentWorks = works;
                    displayWorks(works);

                    const courseListContent = document.getElementById("course-list");
                    const collapseCourseButton = document.getElementById("collapse-course-btn");
                    courseListContent.classList.remove("expanded");
                    collapseCourseButton.textContent = "展开 ▼";

                    const workListContent = document.getElementById("work-list");
                    const collapseWorkButton = document.getElementById("collapse-work-btn");
                    workListContent.classList.add("expanded");
                    collapseWorkButton.textContent = "收起 ▲";
                } else {
                    showNotification("获取作业失败:" + result.msg, "error");
                }
            },
            onerror: (error) => {
                showNotification("网络错误,请检查!", "error");
                console.error(error);
            },
        });
    };

    async function exportAllWorks(courseId) {
        try {
            const works = await getCourseWorksAsync(courseId);
            if (works.length === 0) {
                showNotification("当前课程没有可导出的作业。", "error");
                return;
            }

            for (const work of works) {
                await exportQuestions(work.workId, work.workName);
            }

            showNotification("所有作业已成功导出!", "success");
        } catch (error) {
            console.error("导出作业时出错:", error);
            showNotification("导出作业时出现错误,请检查网络或稍后重试。", "error");
        }
    }

    const getCourseWorksAsync = (courseId) => {
        return new Promise((resolve, reject) => {
            const myCourseWorksUrl = `${baseURL}/myCourseWorks`;
            const data = `courseId=${courseId}`;

            const requestHeaders = {
                ...headers,
            };
            const token = GM_getValue("token", "");
            if (token) {
                requestHeaders.Authorization = token;
            } else {
                showNotification("未检测到登录信息,请先登录!", "error");
                document.getElementById("main-content").style.display = "none";
                document.getElementById("login-prompt").style.display = "block";
                reject(new Error("未登录"));
                return;
            }

            GM_xmlhttpRequest({
                method: "POST",
                url: myCourseWorksUrl,
                headers: requestHeaders,
                data: data,
                onload: (response) => {
                    const result = JSON.parse(response.responseText);
                    if (result.code === 0) {
                        const works = result.data;
                        resolve(works);
                    } else {
                        showNotification("获取作业失败:" + result.msg, "error");
                        reject(new Error("获取作业失败"));
                    }
                },
                onerror: (error) => {
                    showNotification("网络错误,请检查!", "error");
                    console.error(error);
                    reject(error);
                },
            });
        });
    };

    async function exportAllCoursesWorks() {
        try {
            const exportAllCoursesBtn = document.getElementById("export-all-courses-btn");
            exportAllCoursesBtn.disabled = true;
            exportAllCoursesBtn.textContent = "导出中...";

            const courses = await getMyCoursesAsync();
            let successCount = 0;
            let failureCount = 0;

            for (const course of courses) {
                try {
                    const works = await getCourseWorksAsync(course.courseId);

                    if (!works || works.length === 0) {
                        throw new Error(`课程 "${course.courseName}" 没有作业或获取作业失败`);
                    }

                    for (const work of works) {
                        await exportQuestions(work.workId, work.workName);
                    }

                    successCount++;
                    showNotification(`课程 "${course.courseName}" 的作业导出成功。`, "success");

                } catch (error) {
                    console.error(`导出课程 "${course.courseName}" 的作业失败:`, error);
                    failureCount++;
                    showNotification(`课程 "${course.courseName}" 的作业导出失败。`, "error");
                }
            }

            if (successCount > 0 && failureCount === 0) {
                showNotification("所有课程作业已成功导出!", "success");
            } else if (successCount > 0 && failureCount > 0) {
                showNotification(`部分课程作业导出成功。成功: ${successCount}, 失败: ${failureCount}`, "error");
            } else {
                showNotification("所有课程作业导出失败,请检查网络或稍后重试。", "error");
            }

        } catch (error) {
            console.error("导出所有课程作业时出错:", error);
            showNotification("导出所有课程作业时出错,请稍后重试。", "error");
        } finally {
            const exportAllCoursesBtn = document.getElementById("export-all-courses-btn");
            exportAllCoursesBtn.disabled = false;
            exportAllCoursesBtn.textContent = "导出所有课程作业";
        }
    }

    const displayWorks = (works) => {
        const workList = document.getElementById("work-list");
        workList.innerHTML = "";

        works.forEach((work) => {
            const workItem = document.createElement("div");
            workItem.className = "work-item";

            const isCompleted =
                work.times >= work.tryTimes ||
                (work.grade !== null && work.grade >= 60);
            const statusClass = isCompleted ? "completed" : "";
            const gradeText = work.grade !== null ? `分数:${work.grade}` : "";
            workItem.innerHTML = `
                <div>
                    <span title="${work.workName}">${work.workName}</span>
                    <span>(${work.times}/${work.tryTimes} 次)</span>
                </div>
                <div>
                    <div style="display:flex; align-items:center; flex-wrap:wrap;">
                        <span class="status ${statusClass}">${isCompleted ? "已完成" : "未完成"}</span>
                        <span class="grade-text" style="margin-left: 8px;">${gradeText}</span>
                    </div>
                    <div style="margin-top: 5px;">
                        <button class="submit-100-btn" data-work-id="${work.workId}">
                            提交100分
                        </button>
                        <button class="export-questions-btn" data-work-id="${work.workId}" data-work-name="${work.workName}" style="margin-left: 5px;">
                            导出作业
                        </button>
                    </div>
                </div>
            `;
            workList.appendChild(workItem);
        });

        document.querySelectorAll(".submit-100-btn").forEach((btn) => {
            btn.addEventListener("click", (e) => {
                const workId = e.target.getAttribute("data-work-id");
                submitAnswer(workId, 100, e.target);
            });
        });

        document.querySelectorAll(".export-questions-btn").forEach((btn) => {
            btn.addEventListener("click", (e) => {
                const workId = e.target.getAttribute("data-work-id");
                const workName = e.target.getAttribute("data-work-name");
                exportQuestions(workId, workName);
            });
        });
    };

    const updateStatusMessage = (message, show = true) => {
        const statusMessageElement = document.getElementById("status-message");
        statusMessageElement.textContent = message;
        statusMessageElement.style.display = show ? "block" : "none";
    };

    function getQuestions(workId) {
        return new Promise((resolve, reject) => {
            const showQuestionsUrl = `${baseURL}/showQuestions`;
            const data = `workId=${workId}`;

            const requestHeaders = {
                ...headers,
            };
            const token = GM_getValue("token", "");
            if (token) {
                requestHeaders.Authorization = token;
            } else {
                showNotification("未检测到登录信息,请先登录!", "error");
                document.getElementById("main-content").style.display = "none";
                document.getElementById("login-prompt").style.display = "block";
                resolve([]);
                return;
            }

            GM_xmlhttpRequest({
                method: "POST",
                url: showQuestionsUrl,
                headers: requestHeaders,
                data: data,
                onload: (response) => {
                    const result = JSON.parse(response.responseText);
                    if (result.code === 0) {
                        const questions = result.data;
                        resolve(questions);
                    } else {
                        showNotification("获取题目失败:" + result.msg, "error");
                        resolve([]);
                    }
                },
                onerror: (error) => {
                    showNotification("网络错误,请检查!", "error");
                    console.error(error);
                    resolve([]);
                },
            });
        });
    }

    function downloadImage(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                responseType: "arraybuffer",
                onload: (response) => {
                    resolve(response.response);
                },
                onerror: (error) => {
                    console.error("下载图片失败:", error);
                    reject(error);
                },
            });
        });
    }

    async function exportQuestions(workId, workName) {
        try {
            updateStatusMessage(`开始导出作业 "${workName}" 的题目,请稍候...`);

            let collectedQuestions = {};
            let noNewQuestionsCount = 0;
            const maxIterations = 100;

            let progressBar = document.getElementById("export-progress-bar");
            if (progressBar) {
                progressBar.remove();
            }
            progressBar = document.createElement("div");
            progressBar.id = "export-progress-bar";
            progressBar.style.cssText = `
                margin: 20px auto;
                height: 30px;
                width: 80%;
                background-color: #e9ecef;
                border-radius: 15px;
                position: relative;
                overflow: hidden;
                box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
            `;
            document.getElementById("status-message").appendChild(progressBar);

            const progressIndicator = document.createElement("div");
            progressIndicator.style.cssText = `
                height: 100%;
                background: linear-gradient(90deg, #4a90e2, #63b3ed);
                border-radius: 15px;
                width: 0%;
                transition: width 0.5s ease;
                position: relative;
                overflow: hidden;
            `;
            progressBar.appendChild(progressIndicator);

            const lightEffect = document.createElement("div");
            lightEffect.style.cssText = `
                position: absolute;
                top: 0;
                left: 0;
                height: 100%;
                width: 50px;
                background: rgba(255, 255, 255, 0.3);
                box-shadow: 0 0 10px rgba(255, 255, 255, 0.7);
                opacity: 0.6;
                animation: moveLight 1.5s infinite linear;
            `;
            progressIndicator.appendChild(lightEffect);

            const progressText = document.createElement("div");
            progressText.style.cssText = `
                position: absolute;
                top: 5px;
                left: 50%;
                transform: translateX(-50%);
                color: #000;
                font-weight: bold;
                font-size: 14px;
                white-space: nowrap;
                text-overflow: ellipsis;
                overflow: hidden;
                font-family: 'Microsoft YaHei', sans-serif;
                max-width: 90%;
            `;
            progressBar.appendChild(progressText);

            const style = document.createElement('style');
            style.textContent = `
                @keyframes moveLight {
                    0% { left: -50px; }
                    100% { left: 100%; }
                }
            `;
            document.head.appendChild(style);

            for (let i = 0; i < maxIterations; i++) {
                const questions = await getQuestions(workId);
                let newQuestionFound = false;

                for (const question of questions) {
                    const questionId = question.id;
                    if (!collectedQuestions[questionId]) {
                        collectedQuestions[questionId] = question;
                        newQuestionFound = true;
                    }
                }

                const totalQuestions = Object.keys(collectedQuestions).length;
                progressText.textContent = `已收集: ${totalQuestions} 题`;
                progressIndicator.style.width = `${Math.min((totalQuestions / 100) * 100, 100)}%`;

                if (!newQuestionFound) {
                    noNewQuestionsCount += 1;
                    if (noNewQuestionsCount >= 10) {
                        break;
                    }
                } else {
                    noNewQuestionsCount = 0;
                }
            }

            const totalQuestions = Object.keys(collectedQuestions).length;
            if (totalQuestions === 0) {
                showNotification("没有获取到题目,无法导出。", "error");
                updateStatusMessage("", false);
                return;
            }

            const { Document, Paragraph, Packer, HeadingLevel, AlignmentType, ImageRun } = window.docx;

            const doc = new Document({
                creator: "理工学堂助手",
                title: workName,
                description: "由理工学堂助手自动生成",
                sections: []
            });

            const children = [];

            children.push(
                new Paragraph({
                    text: workName,
                    heading: HeadingLevel.HEADING_1,
                    alignment: AlignmentType.CENTER
                })
            );

            const sortedQuestionIds = Object.keys(collectedQuestions).sort((a, b) => a - b);
            for (let i = 0; i < sortedQuestionIds.length; i++) {
                const questionId = sortedQuestionIds[i];
                const question = collectedQuestions[questionId];
                const name = question.name || "N/A";
                const answer = question.answer || "N/A";
                const imgurl = question.imgurl;

                children.push(
                    new Paragraph({
                        text: `题目 ${i + 1}: ${name}`,
                        heading: HeadingLevel.HEADING_2
                    })
                );

                if (imgurl) {
                    try {
                        const imageData = await downloadImage(imgurl);
                        const imageBlob = new Blob([imageData], { type: 'image/png' });

                        const img = await new Promise((resolve, reject) => {
                            const img = new Image();
                            img.onload = () => resolve(img);
                            img.onerror = (err) => reject(err);
                            img.src = URL.createObjectURL(imageBlob);
                        });

                        const originalWidth = img.width;
                        const originalHeight = img.height;

                        const maxWidth = 500;
                        const maxHeight = 500;

                        let width = originalWidth;
                        let height = originalHeight;

                        if (width > maxWidth || height > maxHeight) {
                            const widthRatio = maxWidth / width;
                            const heightRatio = maxHeight / height;
                            const scale = Math.min(widthRatio, heightRatio);
                            width = width * scale;
                            height = height * scale;
                        }

                        const imageDataUrl = await new Promise((resolve) => {
                            const reader = new FileReader();
                            reader.onloadend = () => resolve(reader.result);
                            reader.readAsDataURL(imageBlob);
                        });

                        const image = new ImageRun({
                            data: imageDataUrl,
                            transformation: {
                                width: width,
                                height: height
                            }
                        });

                        children.push(
                            new Paragraph({
                                children: [image],
                                alignment: AlignmentType.CENTER
                            })
                        );
                    } catch (error) {
                        children.push(
                            new Paragraph(`无法下载或处理图片:${error.message}`)
                        );
                    }
                } else {
                    children.push(
                        new Paragraph("(无图片)")
                    );
                }

                children.push(
                    new Paragraph({
                        text: `答案:${answer}`,
                        style: "answerStyle"
                    })
                );

                progressText.textContent = `正在导出第 ${i + 1}/${totalQuestions} 道题目`;
                progressIndicator.style.width = `${Math.min(((i + 1) / totalQuestions) * 100, 100)}%`;
            }

            doc.addSection({
                properties: {},
                children: children
            });

            const blob = await Packer.toBlob(doc);

            window.saveAs(blob, `${workName}.docx`);
            showNotification(`成功导出作业 "${workName}" 的题目,共 ${totalQuestions} 道!`, "success");
            updateStatusMessage("", false);
        } catch (error) {
            console.error(error);
            showNotification("导出过程中出现错误:" + error.message, "error");
            updateStatusMessage("", false);
        }
    }

    function submitAnswer(workId, grade, button) {
        return new Promise((resolve, reject) => {
            const submitAnswerUrl = `${baseURL}/submitAnswer`;
            const data = `workId=${workId}&grade=${grade}`;

            const requestHeaders = {
                ...headers,
            };
            const token = GM_getValue("token", "");
            if (token) {
                requestHeaders.Authorization = token;
            } else {
                showNotification("未检测到登录信息,请先登录!", "error");
                document.getElementById("main-content").style.display = "none";
                document.getElementById("login-prompt").style.display = "block";
                reject(new Error("未登录"));
                return;
            }

            if (button) {
                button.disabled = true;
                button.textContent = "提交中...";
            }

            GM_xmlhttpRequest({
                method: "POST",
                url: submitAnswerUrl,
                headers: requestHeaders,
                data: data,
                onload: (response) => {
                    const result = JSON.parse(response.responseText);
                    if (result.code === 0) {
                        showNotification(`提交成功,成绩:${grade}`, "success");
                        if (button) {
                            button.textContent = "提交100分";
                            button.disabled = false;

                            const gradeSpan = button.closest(".work-item").querySelector(".grade-text");
                            gradeSpan.textContent = `分数:${grade}`;

                            const statusElem = button.closest(".work-item").querySelector(".status");
                            statusElem.classList.add("completed");
                            statusElem.textContent = "已完成";

                            const timesSpan = button.closest(".work-item").querySelector("div > span:nth-child(2)");
                            const timesText = timesSpan.textContent.match(/\d+/g);
                            let currentTimes = parseInt(timesText[0]);
                            let maxTimes = parseInt(timesText[1]);
                            currentTimes = Math.min(currentTimes + 1, maxTimes);
                            timesSpan.textContent = `(${currentTimes}/${maxTimes} 次)`;
                        }
                        resolve();
                    } else {
                        showNotification("提交失败:" + result.msg, "error");
                        if (button) {
                            button.disabled = false;
                            button.textContent = "提交100分";
                        }
                        reject(new Error(result.msg));
                    }
                },
                onerror: (error) => {
                    showNotification("网络错误,请检查!", "error");
                    console.error(error);
                    if (button) {
                        button.disabled = false;
                        button.textContent = "提交100分";
                    }
                    reject(new Error("网络错误"));
                },
            });
        });
    }

    function submitAnswerAsync(workId, grade) {
        return new Promise((resolve, reject) => {
            const submitAnswerUrl = `${baseURL}/submitAnswer`;
            const data = `workId=${workId}&grade=${grade}`;

            const requestHeaders = {
                ...headers,
            };
            const token = GM_getValue("token", "");
            if (token) {
                requestHeaders.Authorization = token;
            } else {
                showNotification("未检测到登录信息,请先登录!", "error");
                document.getElementById("main-content").style.display = "none";
                document.getElementById("login-prompt").style.display = "block";
                reject(new Error("未登录"));
                return;
            }

            GM_xmlhttpRequest({
                method: "POST",
                url: submitAnswerUrl,
                headers: requestHeaders,
                data: data,
                onload: (response) => {
                    const result = JSON.parse(response.responseText);
                    if (result.code === 0) {
                        resolve();
                    } else {
                        showNotification("提交失败:" + result.msg, "error");
                        reject(new Error(result.msg));
                    }
                },
                onerror: (error) => {
                    showNotification("网络错误,请检查!", "error");
                    console.error(error);
                    reject(new Error("网络错误"));
                },
            });
        });
    }

    async function submitAllAnswers() {
        const buttons = document.querySelectorAll(".submit-100-btn");
        const totalWorks = buttons.length;

        if (totalWorks === 0) {
            showNotification("没有可提交的作业!", "error");
            return;
        }

        let progressBar = document.getElementById("progress-bar");
        if (progressBar) {
            progressBar.remove();
        }
        progressBar = document.createElement("div");
        progressBar.id = "progress-bar";
        document.getElementById("submit-all-container").appendChild(progressBar);

        const progressBlocks = [];
        for (let i = 0; i < totalWorks; i++) {
            const block = document.createElement("div");
            block.className = "progress-block";
            progressBar.appendChild(block);
            progressBlocks.push(block);
        }

        for (let i = 0; i < buttons.length; i++) {
            const button = buttons[i];
            const workId = button.getAttribute("data-work-id");
            try {
                await submitAnswer(workId, 100, button);
                progressBlocks[i].classList.add("success");
            } catch (error) {
                console.error(`提交作业 ${workId} 失败:`, error);
                progressBlocks[i].classList.add("failure");
            }
        }
        showNotification("所有可提交的作业已完成!", "success");

        if (progressBar) {
            setTimeout(() => {
                progressBar.remove();
            }, 3000);
        }

    }

    async function submitAllCoursesAnswers() {
        try {
            const submitAllCoursesBtn = document.getElementById("submit-all-courses-btn");
            submitAllCoursesBtn.disabled = true;
            submitAllCoursesBtn.textContent = "提交中...";

            const courses = await getMyCoursesAsync();
            let allWorks = [];

            for (const course of courses) {
                try {
                    const works = await getCourseWorksAsync(course.courseId);
                    allWorks = allWorks.concat(works);
                } catch (error) {
                    console.error(`获取课程 ${course.courseName} 的作业失败:`, error);
                    showNotification(`课程 ${course.courseName} 的作业获取失败,跳过该课程。`, "error");
                }
            }

            const totalWorks = allWorks.length;

            if (totalWorks === 0) {
                showNotification("没有可提交的课程作业!", "error");
                submitAllCoursesBtn.disabled = false;
                submitAllCoursesBtn.textContent = "一键提交所有课程作业100分";
                return;
            }

            let progressBar = document.getElementById("progress-bar");
            if (progressBar) {
                progressBar.remove();
            }
            progressBar = document.createElement("div");
            progressBar.id = "progress-bar";
            document.getElementById("submit-all-courses-container").appendChild(progressBar);

            const progressBlocks = [];
            for (let i = 0; i < totalWorks; i++) {
                const block = document.createElement("div");
                block.className = "progress-block";
                progressBar.appendChild(block);
                progressBlocks.push(block);
            }

            for (let i = 0; i < allWorks.length; i++) {
                const work = allWorks[i];
                try {
                    await submitAnswerAsync(work.workId, 100);
                    progressBlocks[i].classList.add("success");
                } catch (error) {
                    console.error(`提交作业 ${work.workId} 失败:`, error);
                    progressBlocks[i].classList.add("failure");
                }
            }
            showNotification("所有可提交的课程作业已完成!", "success");
            if (progressBar) {
                setTimeout(() => {
                    progressBar.remove();
                }, 3000);
            }
        } catch (error) {
            console.error(error);
            showNotification("提交过程中出现错误:" + error.message, "error");
        } finally {
            const submitAllCoursesBtn = document.getElementById("submit-all-courses-btn");
            submitAllCoursesBtn.disabled = false;
            submitAllCoursesBtn.textContent = "一键提交所有课程作业100分";
        }
    }

    let notificationCount = 0;

    const showNotification = (message, type) => {
        const notification = document.createElement("div");
        notification.classList.add("notification");
        notification.textContent = message;
        notification.style.cssText = `
            position: fixed;
            top: ${20 + notificationCount * 60}px;
            left: 50%;
            transform: translateX(-50%) translateY(-10px);
            padding: 12px 24px;
            border-radius: 8px;
            color: white;
            font-size: 14px;
            font-weight: bold;
            z-index: 10001;
            opacity: 0;
            transition: opacity 0.3s, transform 0.3s, top 0.5s ease;
            font-family: 'Microsoft YaHei', sans-serif;
        `;
        if (type === "success") {
            notification.style.backgroundColor = "#28a745";
        } else if (type === "error") {
            notification.style.backgroundColor = "#dc3545";
        } else {
            notification.style.backgroundColor = "#4a90e2";
        }

        document.body.appendChild(notification);

        setTimeout(() => {
            notification.style.opacity = "1";
            notification.style.transform = "translateX(-50%) translateY(0)";
        }, 10);

        notificationCount++;

        setTimeout(() => {
            notification.style.opacity = "0";
            notification.style.transform = "translateX(-50%) translateY(-10px)";

            setTimeout(() => {
                document.body.removeChild(notification);
                notificationCount--;
                updateNotificationsPosition();
            }, 300);
        }, 3000);
    };

    const updateNotificationsPosition = () => {
        const notifications = document.querySelectorAll('.notification');
        notifications.forEach((notif, index) => {
            notif.style.top = `${20 + index * 60}px`;
        });
    };

    createUI();
})();