// ==UserScript== // @name 方正教务系统成绩分项下载 // @namespace ikaikail@ikaikail.com // @version 1.9 // @description 期末成绩不理想?担心被穿小鞋?不用怕!这款脚本让你期末成绩和平时成绩一目了然!支持VPN环境! // @author iKaiKail // @match *://*/jwglxt/cjcx/* // @include *:/*/cjcx/* // @icon https://www.zfsoft.com/img/zf.ico // @grant GM_addStyle // @require https://code.jquery.com/jquery-3.6.0.min.js // @downloadURL none // ==/UserScript== (function() { 'use strict'; GM_addStyle(` #score-detail-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 1000px; max-height: 80vh; background-color: white; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.3); z-index: 10000; display: none; flex-direction: column; overflow: hidden; } .score-modal-header { padding: 12px 15px; background-color: #2587de; display: flex; justify-content: space-between; align-items: center; border-radius: 8px 8px 0 0; } .score-modal-title { margin: 0; font-size: 16px; font-weight: bold; color: white; } .score-modal-close { background: none; border: none; font-size: 24px; font-weight: 200; opacity: 0.8; cursor: pointer; color: white; padding: 0; line-height: 1; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.3s ease; } .score-modal-close:hover { opacity: 1; background-color: rgba(255,255,255,0.3); } .score-modal-close span { position: relative; top: -2px; } .score-modal-content { padding: 20px; overflow-y: auto; flex-grow: 1; } .result-table { width: 100%; border-collapse: collapse; table-layout: auto; } .result-table th, .result-table td { border: 1px solid #ddd; padding: 8px; text-align: center; white-space: nowrap; } .result-table th { background-color: #f2f2f2; position: sticky; top: -1px; } .col-course-name { word-wrap: break-word; word-break: break-all; text-align: left; padding-left: 10px; white-space: normal; min-width: 180px; } #loading-spinner { border: 5px solid #f3f3f3; border-top: 5px solid #28a745; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .sortable-header { cursor: pointer; user-select: none; } .sortable-header:hover { background-color: #e0e0e0; } .sort-asc::after { content: ''; display: inline-block; margin-left: 5px; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 5px solid #333; } .sort-desc::after { content: ''; display: inline-block; margin-left: 5px; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #333; } .modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 9999; display: none; } .score-action-btn { background-color: #337ab7 !important; color: white !important; border: none !important; } .score-action-btn:hover { background-color: #286090 !important; } #queryScoresBtn { margin-left: 12px; margin-right: 12px; } #exportAllScoresBtn { margin-left: 0; margin-right: 0; } @media (max-width: 767px) { .col-md-4.col-sm-5 button { margin-top: 8px; margin-left: 0 !important; margin-right: 8px !important; } .col-md-4.col-sm-5 button:last-child { margin-right: 0 !important; } } `); const createScoreModal = () => { const modalHTML = `

成绩分项详情

正在加载成绩数据...

`; $('body').append(modalHTML); $('.score-modal-close').click(closeScoreModal); $('#modal-backdrop').click(closeScoreModal); }; const openScoreModal = () => { $('#score-detail-modal, #modal-backdrop').show(); }; const closeScoreModal = () => { $('#score-detail-modal, #modal-backdrop').hide(); }; const createButtons = () => { return { $queryButton: $('`); } }; const handleExport = async () => { try { const xnm = document.getElementById('xnm').value; const xqm = document.getElementById('xqm').value; if (!xnm || !xqm) throw new Error('请先选择学年和学期'); const params = new URLSearchParams([ ['gnmkdmKey', 'N305005'], ['xnm', xnm], ['xqm', xqm], ['dcclbh', 'JW_N305005_GLY'], ...['kcmc@课程名称','xnmmc@学年','xqmmc@学期','kkbmmc@开课学院','kch@课程代码','jxbmc@教学班','xf@学分','xmcj@成绩','xmblmc@成绩分项'].map(col => ['exportModel.selectCol', col]), ['exportModel.exportWjgs', 'xls'], ['fileName', '成绩单'] ]); const targetUrl = `${getBasePath()}/cjcx/cjcx_dcXsKccjList.html`; const response = await fetch(targetUrl, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: params }); if (!response.ok) throw new Error(`服务器返回异常状态码: ${response.status}`); const blob = await response.blob(); downloadFile(blob); const btn = document.getElementById('exportAllScoresBtn'); if (btn) { const originalText = btn.innerHTML; btn.innerHTML = ' 导出成功'; btn.style.backgroundColor = '#218838'; setTimeout(() => { btn.innerHTML = originalText; btn.style.backgroundColor = ''; }, 2000); } } catch (error) { console.error('导出操作失败:', error); const btn = document.getElementById('exportAllScoresBtn'); if (btn) { const originalText = btn.innerHTML; btn.innerHTML = ' 导出失败'; btn.style.backgroundColor = '#dc3545'; setTimeout(() => { btn.innerHTML = originalText; btn.style.backgroundColor = ''; }, 2000); } alert(`导出失败: ${error.message}`); } }; const init = () => { $('#queryScoresBtn, #exportAllScoresBtn').remove(); createScoreModal(); const buttons = createButtons(); buttons.$queryButton.click(handleQuery); buttons.$downloadButton.click(handleExport); const $searchButton = $('#search_go'); if ($searchButton.length) $searchButton.after(buttons.$downloadButton).after(buttons.$queryButton); else if ($('.panel-info:has(.panel-body)').length) $('.panel-info:has(.panel-body)').find('.panel-body').append(buttons.$queryButton).append(buttons.$downloadButton); else if ($('form:has(#xnm)').length) $('form:has(#xnm)').append(buttons.$queryButton).append(buttons.$downloadButton); else $('body').prepend(buttons.$queryButton).prepend(buttons.$downloadButton); }; const checkDOM = () => { if ($('#xnm, #xqm').length >= 2) init(); else setTimeout(checkDOM, 500); }; setTimeout(checkDOM, 1000); $(document).ajaxStop(function() { if (!$('#queryScoresBtn').length) setTimeout(init, 500); }); })();