// ==UserScript== // @name 正方教务教学评价 // @namespace https://scriptcat.org/ // @version 2.0.2 // @description 支持批量评价、自动切换课程、检测绕过的教学评价自动化脚本 // @author 征途wd // @icon https://free.picui.cn/free/2025/12/18/6943e6aa2919b.ico // @match https://*.edu.cn/jwglxt/xspjgl/xspj_cxXspjIndex.html* // @match http://*.edu.cnw/jwglxt/xspjgl/xspj_cxXspjIndex.html* // @grant none // @license MIT // @supportURL https://bbs.tampermonkey.net.cn/ // @homepageURL https://space.bilibili.com/353379484 // ==/UserScript== (function () { const DEBUG_MODE = false; const Version = '2.0.2'; const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); const CSS_STYLES = { button: { base: ` width: 100%; height: 35px; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 11px; font-weight: bold; transition: all 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; `, hover: { opacity: '0.8', transform: 'scale(1.02)' }, normal: { opacity: '1', transform: 'scale(1)' } }, panel: { main: ` position: fixed; left: 50%; top: 10px; transform: translateX(-50%); z-index: 9999; background: rgba(30, 30, 30, 0.85); backdrop-filter: blur(15px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); width: 300px; height: 650px; max-height: 90vh; min-width: 200px; min-height: 200px; overflow-y: auto; cursor: move; user-select: none; color: white; font-family: 'Consolas', 'Monaco', monospace; display: flex; flex-direction: column; `, titleBar: ` display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 2px solid #e0e0e0; `, title: ` margin: 0; color: #fff; font-size: 16px; font-weight: bold; `, authorInfo: ` display: flex; align-items: center; gap: 8px; margin-top: 5px; font-size: 12px; color: #ccc; `, authorLink: ` color: #00a1d6; text-decoration: none; cursor: pointer; transition: color 0.3s; ` }, controls: { minimizeBtn: ` background: rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 4px; width: 30px; height: 30px; cursor: pointer; font-size: 20px; line-height: 1; color: white; transition: all 0.3s; `, clearLogBtn: ` background: rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 4px; width: 30px; height: 30px; cursor: pointer; font-size: 14px; line-height: 1; color: white; transition: all 0.3s; margin-right: 5px; ` }, settings: { area: ` margin-bottom: 10px; padding: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.2); `, title: ` font-size: 14px; font-weight: bold; color: #fff; margin-bottom: 8px; `, radioGroup: ` display: flex; gap: 20px; align-items: center; `, radioLabel: ` display: flex; align-items: center; cursor: pointer; font-size: 13px; color: #fff; `, divider: ` margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.2); `, select: ` width: 100%; padding: 4px 8px; border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 4px; background: rgba(255, 255, 255, 0.1); color: #fff; font-size: 12px; ` }, buttonContainer: ` display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 6px; margin-bottom: 10px; `, log: { area: ` margin-top: 10px; padding: 10px; background: rgba(0, 0, 0, 0.3); border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.2); min-height: 120px; overflow-y: auto; font-size: 12px; line-height: 1.4; flex: 1; display: flex; flex-direction: column; `, title: ` font-size: 13px; font-weight: bold; color: #4CAF50; margin-bottom: 5px; border-bottom: 1px solid rgba(255, 255, 255, 0.2); padding-bottom: 3px; `, content: ` color: #e0e0e0; font-family: 'Consolas', 'Monaco', monospace; white-space: pre-wrap; word-break: break-all; flex: 1; overflow-y: auto; `, line: (color) => ` color: ${color}; margin-bottom: 2px; padding: 2px 0; border-left: 3px solid ${color}; padding-left: 8px; margin-left: 5px; flex-shrink: 0; ` }, resizeHandle: ` position: absolute; bottom: 0; right: 0; width: 20px; height: 20px; background: linear-gradient(-45deg, transparent 30%, rgba(255,255,255,0.4) 30%, rgba(255,255,255,0.4) 70%, transparent 70%); cursor: nw-resize; border-bottom-right-radius: 12px; transition: all 0.3s ease; `, status: { submitted: ` margin-top: 10px; padding: 8px 12px; background: rgba(76, 175, 80, 0.2); border: 1px solid rgba(76, 175, 80, 0.5); border-radius: 6px; color: #4CAF50; font-size: 13px; text-align: center; font-weight: bold; ` }, notification: ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px 15px; border-radius: 5px; z-index: 10000; font-size: 14px; `, colors: { excellent: '#28a745', good: '#6f42c1', qualified: '#007bff', unqualified: '#dc3545', batch: '#17a2b8', navigation: '#795548', donate: '#FFD700', debug: '#6c757d' }, hover: { authorLink: { color: '#40c5ff' }, minimizeBtn: { background: 'rgba(255, 255, 255, 0.3)' }, clearLogBtn: { background: 'rgba(255, 100, 100, 0.3)' }, resizeHandle: { background: 'linear-gradient(-45deg, transparent 20%, rgba(255,255,255,0.6) 20%, rgba(255,255,255,0.6) 80%, transparent 80%)' } } }; const applyStyles=(element,styleString)=>{element.style.cssText=styleString};const addHoverEffect=(element,hoverStyles,normalStyles)=>{element.addEventListener('mouseenter',()=>{Object.assign(element.style,hoverStyles)});element.addEventListener('mouseleave',()=>{Object.assign(element.style,normalStyles)})};const createButton=(id,text,color)=>{const button=document.createElement('button');button.id=id;applyStyles(button,CSS_STYLES.button.base+`background-color:${color};`);button.innerText=text;addHoverEffect(button,CSS_STYLES.button.hover,CSS_STYLES.button.normal);return button};const addButton=(parent,button)=>{parent.appendChild(button)};const selectAllItems=(doc,optionIndex)=>{let selectedCount=0;for(let i=optionIndex;i{const saveBtn=document.querySelector('#btn_xspj_bc')||document.querySelector('button[onclick*="baocun"]')||document.querySelector('input[value*="保存"]');if(saveBtn){if(!$(saveBtn).data("enter")){$(saveBtn).data("enter","1")}const mouseEnterEvent=new MouseEvent('mouseenter',{bubbles:true,cancelable:true,view:window});saveBtn.dispatchEvent(mouseEnterEvent);setTimeout(()=>{saveBtn.click();logSuccess('✅ 已自动保存评价')},100);return true}else{logError('❌ 未找到保存按钮');return false}};const autoSubmit=()=>{const saved=autoSave();if(saved){setTimeout(()=>{const submitBtn=document.querySelector('#btn_xspj_tj')||document.querySelector('button[onclick*="tijiao"]')||document.querySelector('input[value*="提交"]')||document.querySelector('button:contains("提交")');if(submitBtn){if(!$(submitBtn).data("enter")){$(submitBtn).data("enter","1")}const mouseEnterEvent=new MouseEvent('mouseenter',{bubbles:true,cancelable:true,view:window});submitBtn.dispatchEvent(mouseEnterEvent);setTimeout(()=>{submitBtn.click();logSuccess('✅ 已自动提交评价')},100)}else{logError('❌ 未找到提交按钮')}},1000)}};const configPanel=document.createElement('div');configPanel.id='evalConfigPanel';applyStyles(configPanel,CSS_STYLES.panel.main);const titleBar=document.createElement('div');applyStyles(titleBar,CSS_STYLES.panel.titleBar);const title=document.createElement('h4');applyStyles(title,CSS_STYLES.panel.title);title.textContent=`🎯评价助手v${Version}`;const authorInfo=document.createElement('div');applyStyles(authorInfo,CSS_STYLES.panel.authorInfo);const authorText=document.createElement('span');authorText.textContent='by:';const authorLink=document.createElement('a');authorLink.href='https://space.bilibili.com/353379484';authorLink.target='_blank';authorLink.textContent='征途wd';applyStyles(authorLink,CSS_STYLES.panel.authorLink);addHoverEffect(authorLink,CSS_STYLES.hover.authorLink,{color:'#00a1d6'});authorInfo.appendChild(authorText);authorInfo.appendChild(authorLink);const minimizeBtn=document.createElement('button');minimizeBtn.textContent='−';applyStyles(minimizeBtn,CSS_STYLES.controls.minimizeBtn);addHoverEffect(minimizeBtn,CSS_STYLES.hover.minimizeBtn,{background:'rgba(255, 255, 255, 0.2)'});const clearLogBtn=document.createElement('button');clearLogBtn.textContent='🗑️';clearLogBtn.title='清空日志';applyStyles(clearLogBtn,CSS_STYLES.controls.clearLogBtn);addHoverEffect(clearLogBtn,CSS_STYLES.hover.clearLogBtn,{background:'rgba(255, 255, 255, 0.2)'});const buttonGroup=document.createElement('div');buttonGroup.style.display='flex';buttonGroup.appendChild(clearLogBtn);buttonGroup.appendChild(minimizeBtn);const titleContainer=document.createElement('div');titleContainer.appendChild(title);titleContainer.appendChild(authorInfo);titleBar.appendChild(titleContainer);titleBar.appendChild(buttonGroup);const settingsArea=document.createElement('div');applyStyles(settingsArea,CSS_STYLES.settings.area);const settingsTitle=document.createElement('div');applyStyles(settingsTitle,CSS_STYLES.settings.title);settingsTitle.textContent='⚙️ 自动操作设置';const radioGroup=document.createElement('div');applyStyles(radioGroup,CSS_STYLES.settings.radioGroup);const saveRadio=document.createElement('label');applyStyles(saveRadio,CSS_STYLES.settings.radioLabel);saveRadio.innerHTML=`自动保存`;const submitRadio=document.createElement('label');applyStyles(submitRadio,CSS_STYLES.settings.radioLabel);submitRadio.innerHTML=`自动提交`;const noneRadio=document.createElement('label');applyStyles(noneRadio,CSS_STYLES.settings.radioLabel);noneRadio.innerHTML=`不自动操作`;radioGroup.appendChild(saveRadio);radioGroup.appendChild(submitRadio);radioGroup.appendChild(noneRadio);const autoSwitchDiv=document.createElement('div');applyStyles(autoSwitchDiv,CSS_STYLES.settings.divider);const autoSwitchLabel=document.createElement('label');applyStyles(autoSwitchLabel,CSS_STYLES.settings.radioLabel);autoSwitchLabel.innerHTML=`自动跳过已提交课程`;autoSwitchDiv.appendChild(autoSwitchLabel);const autoNextDiv=document.createElement('div');applyStyles(autoNextDiv,CSS_STYLES.settings.divider);const autoNextLabel=document.createElement('label');applyStyles(autoNextLabel,CSS_STYLES.settings.radioLabel);autoNextLabel.innerHTML=`评价后自动切换下一个`;autoNextDiv.appendChild(autoNextLabel);const batchLevelDiv=document.createElement('div');applyStyles(batchLevelDiv,CSS_STYLES.settings.divider);const batchLevelTitle=document.createElement('div');applyStyles(batchLevelTitle,CSS_STYLES.settings.title.replace('14px','12px').replace('8px','5px'));batchLevelTitle.textContent='批量评价等级:';const batchLevelSelect=document.createElement('select');batchLevelSelect.id='batchEvalLevel';applyStyles(batchLevelSelect,CSS_STYLES.settings.select);const levelOptions=[['🌟 优秀 (100分)','100'],['👍 良好 (85分)','85'],['✅ 合格 (75分)','75'],['❌ 不合格 (50分)','50']];levelOptions.forEach(([text,score],index)=>{const option=document.createElement('option');Object.assign(option,{value:index,textContent:text});option.setAttribute('data-score',score);batchLevelSelect.appendChild(option)});batchLevelSelect.value='0';batchLevelDiv.appendChild(batchLevelTitle);batchLevelDiv.appendChild(batchLevelSelect);settingsArea.appendChild(settingsTitle);settingsArea.appendChild(radioGroup);settingsArea.appendChild(autoSwitchDiv);settingsArea.appendChild(autoNextDiv);settingsArea.appendChild(batchLevelDiv);const btnContainer=document.createElement('div');applyStyles(btnContainer,CSS_STYLES.buttonContainer);const btna=createButton('btna','🌟 优秀',CSS_STYLES.colors.excellent);const btnb=createButton('btnb','👍 良好',CSS_STYLES.colors.good);const btnc=createButton('btnc','✅ 合格',CSS_STYLES.colors.qualified);const btnd=createButton('btnd','❌ 不合格',CSS_STYLES.colors.unqualified);const btnAuto=createButton('btnAuto','⚡ 批量评价',CSS_STYLES.colors.batch);const btnPrev=createButton('btnPrev','⬅️ 上一个',CSS_STYLES.colors.navigation);const btnNext=createButton('btnNext','➡️ 下一个',CSS_STYLES.colors.navigation);const btnDonate=createButton('btnDonate','💰 打赏作者',CSS_STYLES.colors.donate);addButton(btnContainer,btna);addButton(btnContainer,btnb);addButton(btnContainer,btnc);addButton(btnContainer,btnd);addButton(btnContainer,btnAuto);addButton(btnContainer,btnPrev);addButton(btnContainer,btnNext);addButton(btnContainer,btnDonate);const logArea=document.createElement('div');applyStyles(logArea,CSS_STYLES.log.area);const logTitle=document.createElement('div');applyStyles(logTitle,CSS_STYLES.log.title);logTitle.textContent='📋 运行日志';const logContent=document.createElement('div');logContent.id='scriptLog';applyStyles(logContent,CSS_STYLES.log.content);logArea.appendChild(logTitle);logArea.appendChild(logContent);const resizeHandle=document.createElement('div');applyStyles(resizeHandle,CSS_STYLES.resizeHandle);addHoverEffect(resizeHandle,CSS_STYLES.hover.resizeHandle,{background:'linear-gradient(-45deg, transparent 30%, rgba(255,255,255,0.4) 30%, rgba(255,255,255,0.4) 70%, transparent 70%)'});configPanel.appendChild(titleBar);configPanel.appendChild(logArea);configPanel.appendChild(settingsArea);configPanel.appendChild(btnContainer);configPanel.appendChild(resizeHandle);document.body.appendChild(configPanel); let isDragging = false; let isResizing = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; titleBar.addEventListener('mousedown', dragStart); resizeHandle.addEventListener('mousedown', resizeStart); document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); function dragStart(e) { if (e.target === titleBar || e.target === title) { if (isMinimized) { minimizeBtn.click(); return; } initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; isDragging = true; e.preventDefault(); } } function resizeStart(e) { isResizing = true; initialX = e.clientX; initialY = e.clientY; e.preventDefault(); e.stopPropagation(); } function handleMouseMove(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; configPanel.style.transform = `translate(calc(-50% + ${currentX}px), ${currentY}px)`; } else if (isResizing) { e.preventDefault(); const deltaX = e.clientX - initialX; const deltaY = e.clientY - initialY; const currentWidth = parseInt(configPanel.style.width) || 350; let currentHeight; if (configPanel.style.height === 'auto' || !configPanel.style.height) { currentHeight = configPanel.offsetHeight; } else { currentHeight = parseInt(configPanel.style.height); } const newWidth = Math.max(300, currentWidth + deltaX); const newHeight = Math.max(200, currentHeight + deltaY); configPanel.style.width = newWidth + 'px'; configPanel.style.height = newHeight + 'px'; configPanel.style.overflowY = 'auto'; updateLogAreaLayout(); initialX = e.clientX; initialY = e.clientY; } } function handleMouseUp(e) { if (isDragging) { initialX = currentX; initialY = currentY; } isDragging = false; isResizing = false; } const updateLogAreaLayout = () => { const logElement = document.getElementById('scriptLog'); if (logElement) { setTimeout(() => { logElement.scrollTop = logElement.scrollHeight; }, 10); } }; const originalConsoleLog = console.log; const logToPanel = (message, type = 'info') => { const timestamp = new Date().toLocaleTimeString(); const logElement = document.getElementById('scriptLog'); if (logElement) { const colors = { info: '#e0e0e0', success: '#4CAF50', warning: '#FF9800', error: '#f44336', debug: '#2196F3' }; const logLine = document.createElement('div'); applyStyles(logLine, CSS_STYLES.log.line(colors[type] || colors.info)); logLine.textContent = `[${timestamp}] ${message}`; logElement.appendChild(logLine); updateLogAreaLayout(); while (logElement.children.length > 150) { logElement.removeChild(logElement.firstChild); } } }; console.log = function(...args) { const message = args.join(' '); logToPanel(message, 'info'); originalConsoleLog.apply(console, args); }; const logFunctions = { logSuccess: 'success', logWarning: 'warning', logError: 'error' }; Object.entries(logFunctions).forEach(([name, type]) => { window[name] = (message) => logToPanel(message, type); }); window.logDebug = (message) => { if (DEBUG_MODE) { logToPanel(message, 'debug'); } }; clearLogBtn.addEventListener('click', () => { const logElement = document.getElementById('scriptLog'); if (logElement) { logElement.innerHTML = ''; logSuccess('日志已清空'); updateLogAreaLayout(); } }); let isMinimized = false; let originalWidth = '300px'; let originalHeight = '650px'; minimizeBtn.addEventListener('click', () => { isMinimized = !isMinimized; if (isMinimized) { originalWidth = configPanel.style.width || '300px'; originalHeight = configPanel.style.height || '650px'; settingsArea.style.display = 'none'; btnContainer.style.display = 'none'; logArea.style.display = 'none'; resizeHandle.style.display = 'none'; const statusDiv = document.getElementById('submissionStatusDiv'); if (statusDiv) { statusDiv.style.display = 'none'; } minimizeBtn.textContent = '📌'; minimizeBtn.title = '展开面板'; title.textContent = '🎯 评价助手'; authorInfo.style.display = 'none'; configPanel.style.width = '140px'; configPanel.style.height = 'auto'; configPanel.style.minHeight = 'auto'; configPanel.style.padding = '8px 12px'; configPanel.style.flexDirection = 'row'; configPanel.style.cursor = 'pointer'; titleBar.style.marginBottom = '0'; titleBar.style.paddingBottom = '0'; titleBar.style.borderBottom = 'none'; titleBar.style.justifyContent = 'flex-start'; titleBar.style.gap = '8px'; titleBar.style.cursor = 'pointer'; } else { settingsArea.style.display = 'block'; btnContainer.style.display = 'grid'; logArea.style.display = 'flex'; resizeHandle.style.display = 'block'; const statusDiv = document.getElementById('submissionStatusDiv'); if (statusDiv) { statusDiv.style.display = 'block'; } minimizeBtn.textContent = '−'; minimizeBtn.title = '最小化面板'; title.textContent = `🎯 评价助手 v${Version}`; authorInfo.style.display = 'flex'; configPanel.style.width = originalWidth; configPanel.style.height = originalHeight; configPanel.style.minHeight = '200px'; configPanel.style.padding = '15px'; configPanel.style.flexDirection = 'column'; configPanel.style.cursor = 'move'; titleBar.style.marginBottom = '10px'; titleBar.style.paddingBottom = '10px'; titleBar.style.borderBottom = '2px solid #e0e0e0'; titleBar.style.justifyContent = 'space-between'; titleBar.style.gap = ''; titleBar.style.cursor = 'move'; setTimeout(() => { const logElement = document.getElementById('scriptLog'); if (logElement && logElement.scrollHeight > logElement.clientHeight) { logElement.scrollTop = logElement.scrollHeight; } }, 100); } }); const getAutoAction=()=>{const selected=document.querySelector('input[name="autoAction"]:checked');return selected?selected.value:'save'};const getAutoSwitchSetting=()=>{const checkbox=document.getElementById('autoSwitchCourse');return checkbox?checkbox.checked:false};const getAutoNextAfterEvalSetting=()=>{const checkbox=document.getElementById('autoNextAfterEval');return checkbox?checkbox.checked:false};const getBatchEvalLevel=()=>{const select=document.getElementById('batchEvalLevel');if(!select)return{index:0,score:'100',name:'优秀'};const selectedOption=select.options[select.selectedIndex];const levelNames=['优秀','良好','合格','不合格'];return{index:parseInt(select.value),score:selectedOption.getAttribute('data-score'),name:levelNames[parseInt(select.value)]}};const switchCourse=(direction)=>{logSuccess(`🔄正在切换到${direction==='next'?'下一个':'上一个'}课程...`);try{const grid=$('#tempGrid');if(grid.length>0){const currentRowId=grid.jqGrid('getGridParam','selrow');const allRowIds=grid.jqGrid('getDataIDs');logDebug(`当前选中行ID:${currentRowId},总行数:${allRowIds.length}`);if(allRowIds.length>0){let targetRowId=null;const currentIndex=allRowIds.indexOf(currentRowId);if(direction==='next'){for(let i=currentIndex+1;i=0;i--){const rowData=grid.jqGrid('getRowData',allRowIds[i]);if(getAutoSwitchSetting()&&(rowData.tjztmc==='提交')){logDebug(`跳过已提交课程:${rowData.jxbmc}`);continue}targetRowId=allRowIds[i];break}if(!targetRowId){for(let i=allRowIds.length-1;i>currentIndex;i--){const rowData=grid.jqGrid('getRowData',allRowIds[i]);if(getAutoSwitchSetting()&&(rowData.tjztmc==='提交')){continue}targetRowId=allRowIds[i];break}}}if(targetRowId){const targetRowData=grid.jqGrid('getRowData',targetRowId);logSuccess(`🎯切换到课程:${targetRowData.jxbmc}(${targetRowData.jzgmc})`);grid.jqGrid('setSelection',targetRowId);setTimeout(()=>{const row=$(`#${targetRowId}`);if(row.length>0){row.trigger('click');setTimeout(()=>{logDebug('🔍 重新检测课程提交状态...');displaySubmissionStatus()},2000)}},500);return true}else{if(getAutoSwitchSetting()){logWarning('❌ 没有找到未提交的课程')}else{logWarning('❌ 已经是最后一个课程')}return false}}}const courseRows=document.querySelectorAll('#tempGrid tr[role="row"]');if(courseRows.length>1){logDebug(`找到${courseRows.length-1}个课程行`);const selectedRow=document.querySelector('#tempGrid tr.ui-state-highlight');if(selectedRow){const allRows=Array.from(courseRows).slice(1);const currentIndex=allRows.indexOf(selectedRow);let targetIndex=-1;if(direction==='next'){targetIndex=(currentIndex+1)%allRows.length}else{targetIndex=currentIndex>0?currentIndex-1:allRows.length-1}if(targetIndex>=0&&allRows[targetIndex]){const targetRow=allRows[targetIndex];targetRow.click();const courseName=targetRow.querySelector('td[aria-describedby*="jxbmc"]')?.textContent||'未知课程';const teacherName=targetRow.querySelector('td[aria-describedby*="jzgmc"]')?.textContent||'未知教师';logSuccess(`🎯切换到课程:${courseName}(${teacherName})`);setTimeout(()=>{logDebug('🔍 重新检测课程提交状态...');displaySubmissionStatus()},2000);return true}}}return true}catch(error){logError(`❌课程切换失败:${error.message}`);return false}};const autoSkipSubmittedCourse=()=>{if(!getAutoSwitchSetting()){return false}if(checkSubmissionStatus()){logWarning('🔄 检测到已提交课程,自动跳转到下一个...');setTimeout(()=>{const switched=switchCourse('next');if(!switched){logWarning('🚫 无法自动切换,可能已经是最后一个课程')}},2000);return true}return false};const checkSubmissionStatus=()=>{const tjztElement=document.getElementById('tjzt');if(tjztElement&&tjztElement.value==='1'){return true}const radioButtons=document.getElementsByClassName("radio-pjf");if(radioButtons.length===0){return true}const evaluationRows=document.querySelectorAll('.tr-xspj td:nth-child(2)');if(evaluationRows.length>0){const firstRow=evaluationRows[0];const hasInputs=firstRow.querySelector('input, select, textarea');if(!hasInputs&&firstRow.textContent.trim().length>0){return true}}return false};const displaySubmissionStatus=()=>{const isSubmitted=checkSubmissionStatus();const existingStatus=settingsArea.parentNode.querySelector('div[style*="rgba(76, 175, 80, 0.2)"]');if(existingStatus){existingStatus.remove()}if(isSubmitted){const statusDiv=document.createElement('div');statusDiv.id='submissionStatusDiv';applyStyles(statusDiv,CSS_STYLES.status.submitted);statusDiv.innerHTML='✅ 评价已提交完成';const buttons=btnContainer.querySelectorAll('button');buttons.forEach(btn=>{btn.disabled=false;btn.style.opacity='1';btn.style.cursor='pointer';btn.title=''});buttons.forEach(btn=>{if(btn.id!=='btnDebug'&&btn.id!=='btnAuto'&&btn.id!=='btnPrev'&&btn.id!=='btnNext'&&btn.id!=='btnDonate'){btn.disabled=true;btn.style.opacity='0.5';btn.style.cursor='not-allowed';btn.title='评价已提交,无法再次操作'}});const radioInputs=settingsArea.querySelectorAll('input[type="radio"]');radioInputs.forEach(radio=>{radio.disabled=false});settingsArea.parentNode.insertBefore(statusDiv,btnContainer);logWarning('🚫 检测到评价已提交,评价功能已禁用');const scoreElement=document.querySelector('.xspjSum');if(scoreElement){const scoreText=scoreElement.textContent||scoreElement.innerText;logSuccess('📊 '+scoreText)}setTimeout(()=>{autoSkipSubmittedCourse()},1000);return true}else{const buttons=btnContainer.querySelectorAll('button');buttons.forEach(btn=>{btn.disabled=false;btn.style.opacity='1';btn.style.cursor='pointer';btn.title=''});const radioInputs=settingsArea.querySelectorAll('input[type="radio"]');radioInputs.forEach(radio=>{radio.disabled=false});if(btnAuto){btnAuto.innerText='⚡ 批量评价';btnAuto.title='批量评价所有未提交课程';btnAuto.style.backgroundColor=CSS_STYLES.colors.batch}logSuccess('✅ 评价未提交,功能正常可用');return false}};if(DEBUG_MODE){logDebug('🔧 调试模式已开启,调试按钮可用')}logSuccess(`🎯正方教务评价脚本已加载-脚本猫版本v${Version}`);logSuccess('📝 使用说明:点击对应按钮进行评价选择');logSuccess('🚀 批量评价:可在设置中选择评价等级,批量处理所有未提交课程');const adjustPanelHeight=()=>{setTimeout(()=>{const panelContent=configPanel.scrollHeight;const viewportHeight=window.innerHeight*0.9;const defaultHeight=650;if(panelContent>viewportHeight){configPanel.style.height=viewportHeight+'px';configPanel.style.overflowY='auto'}else if(panelContent>defaultHeight){configPanel.style.height='auto';configPanel.style.overflowY='visible'}else{configPanel.style.height=defaultHeight+'px';configPanel.style.overflowY='visible'}},100)};setTimeout(()=>{displaySubmissionStatus();if(btnAuto){btnAuto.innerText='⚡ 批量评价';btnAuto.title='批量评价所有未提交课程';btnAuto.style.backgroundColor='#17a2b8'}adjustPanelHeight()},500);setTimeout(()=>{const autoSwitchCheckbox=document.getElementById('autoSwitchCourse');if(autoSwitchCheckbox){autoSwitchCheckbox.addEventListener('change',function(){const isEnabled=this.checked;logSuccess(`🔄自动跳过已提交课程:${isEnabled?'已开启':'已关闭'}`);if(isEnabled&&checkSubmissionStatus()){logWarning('🔄 当前课程已提交,即将自动跳转...');setTimeout(()=>{switchCourse('next')},1000)}})}const autoNextCheckbox=document.getElementById('autoNextAfterEval');if(autoNextCheckbox){autoNextCheckbox.addEventListener('change',function(){const isEnabled=this.checked;logSuccess(`⏭️评价后自动切换下一个:${isEnabled?'已开启':'已关闭'}`);if(isEnabled){logSuccess('💡 提示: 点击评价按钮后将自动切换到下一个课程')}else{logSuccess('💡 提示: 评价后需要手动切换课程')}})}const batchLevelSelect=document.getElementById('batchEvalLevel');if(batchLevelSelect){batchLevelSelect.addEventListener('change',function(){const evalLevel=getBatchEvalLevel();logSuccess(`🎯批量评价等级已设置为:${evalLevel.name}(${evalLevel.score}分)`)})}},1000);const bypassDetection=()=>{if(window.$&&$.i18n&&$.i18n.get){const originalGet=$.i18n.get;$.i18n.get=function(key){if(key==='qwsyjbzr'){logWarning('🛡️ 已拦截脚本注入检测');return''}return originalGet.call(this,key)}}Object.defineProperty(navigator,'userAgent',{get:function(){return'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'},configurable:true});window.inProgress=false;const simulateMouseEvents=()=>{document.addEventListener('click',function(e){if(e.target.id==='btn_xspj_bc'||e.target.id==='btn_xspj_tj'){if(!$(e.target).data("enter")){$(e.target).data("enter","1")}}},true)};simulateMouseEvents();logDebug('🛡️ 检测绕过系统已启用')};bypassDetection();window.addEventListener('resize',()=>{updateLogAreaLayout()});if(window.ResizeObserver){const resizeObserver=new ResizeObserver(entries=>{for(let entry of entries){if(entry.target===configPanel){updateLogAreaLayout()}}});resizeObserver.observe(configPanel)}const observePageChanges=()=>{const targetNode=document.getElementById('panel_content');if(targetNode){const observer=new MutationObserver((mutations)=>{let shouldRecheck=false;mutations.forEach((mutation)=>{if(mutation.type==='childList'||mutation.type==='subtree'){if(mutation.target.querySelector('.xspjSum')||mutation.target.querySelector('#tjzt')||mutation.target.querySelector('.radio-pjf')){shouldRecheck=true}}});if(shouldRecheck){logDebug('🔍 检测到页面内容变化,重新检测提交状态...');setTimeout(()=>{displaySubmissionStatus()},1000)}});observer.observe(targetNode,{childList:true,subtree:true,attributes:true,attributeFilter:['value']});logDebug('📡 页面变化监听器已启动')}};setTimeout(observePageChanges,2000);const interceptAjaxDetection=()=>{if(window.jQuery&&jQuery.post){const originalPost=jQuery.post;jQuery.post=function(url,data,callback,type){if(url&&(url.includes('cxInsjjg')||url.includes('cxSftf')||url.includes('cxInmfzb')||url.includes('cxInmffz'))){logDebug('🛡️ 已拦截检测请求: '+url);if(typeof callback==='function'){setTimeout(()=>callback(0),100)}return}return originalPost.apply(this,arguments)}}if(window.isPc2){window.isPc2=function(){return true}}if(window.detectMobileOrTablet){window.detectMobileOrTablet=function(){return"desktop"}}logDebug('🛡️ AJAX检测拦截已启用')};setTimeout(interceptAjaxDetection,1000);const originalAlert=window.alert;window.alert=function(message){logWarning('🚨 [系统弹窗] Alert: '+message);if(message.includes('脚本注入')||message.includes('qwsyjbzr')){logWarning('🛡️ 已拦截脚本检测警告');return}return originalAlert.call(window,message)};const originalConfirm=window.confirm;window.confirm=function(message){logWarning('❓ [系统弹窗] Confirm: '+message);const result=originalConfirm.call(window,message);logSuccess('✅ [用户选择]: '+(result?'确定':'取消'));return result};const handleEvaluation=async(index,score,name)=>{if(checkSubmissionStatus()){logError('❌ 评价已提交,无法再次操作');return}await sleep(200);const doc=document.getElementsByClassName("radio-pjf");const input=document.getElementsByClassName("input-sm input-pjf");if(doc.length===0){logError('❌ 未找到评价选项,可能已提交');return}if(input.length>0){input[0].value=score}selectAllItems(doc,index);logSuccess(`✅已选择${name}评价`);await sleep(800);const action=getAutoAction();if(action==='save'){autoSave()}else if(action==='submit'){autoSubmit()}if(getAutoNextAfterEvalSetting()){logSuccess('🔄 评价完成,准备自动切换到下一个课程...');setTimeout(()=>{const switched=switchCourse('next');if(!switched){logWarning('❌ 无法自动切换,可能已经是最后一个课程')}},2000)}};btna.addEventListener('click',()=>handleEvaluation(0,"100","优秀"));btnb.addEventListener('click',()=>handleEvaluation(1,"85","良好"));btnc.addEventListener('click',()=>handleEvaluation(2,"75","合格"));btnd.addEventListener('click',()=>handleEvaluation(3,"50","不合格"));const batchEvaluateAllCourses=async()=>{logSuccess('🚀 开始批量评价所有未提交课程...');const grid=$('#tempGrid');if(grid.length===0){logError('❌ 未找到课程列表');return false}const allRowIds=grid.jqGrid('getDataIDs');const unsubmittedCourses=[];for(let rowId of allRowIds){const rowData=grid.jqGrid('getRowData',rowId);if(rowData.tjztmc!=='提交'){unsubmittedCourses.push({id:rowId,name:rowData.jxbmc,teacher:rowData.jzgmc,status:rowData.tjztmc})}}if(unsubmittedCourses.length===0){logWarning('🎉 所有课程都已提交完成!');return true}logSuccess(`📋找到${unsubmittedCourses.length}个未提交课程,开始批量评价...`);let successCount=0;let failCount=0;for(let i=0;i0){row.trigger('click');await sleep(2000);}if(checkSubmissionStatus()){logWarning(`⚠️课程${course.name}已提交,跳过`);continue;}const doc=document.getElementsByClassName("radio-pjf");const input=document.getElementsByClassName("input-sm input-pjf");if(doc.length===0){logError(`❌课程${course.name}未找到评价选项`);failCount++;continue;}const evalLevel=getBatchEvalLevel();if(input.length>0){input[0].value=evalLevel.score;}selectAllItems(doc,evalLevel.index);logSuccess(`✅已完成课程${course.name}的${evalLevel.name}评价`);await sleep(800);const action=getAutoAction();if(action==='save'){autoSave();await sleep(1500);}else if(action==='submit'){autoSubmit();await sleep(2500);}else{autoSave();await sleep(1500);}successCount++;logSuccess(`🎯课程${course.name}评价完成(${successCount}/${unsubmittedCourses.length})`);await sleep(1000)}catch(error){logError(`❌评价课程${course.name}时出错:${error.message}`);failCount++}}logSuccess('🏁 批量评价完成!');logSuccess(`📊成功:${successCount}个课程`);if(failCount>0){logWarning(`⚠️失败:${failCount}个课程`)}logSuccess('🔄 正在刷新页面状态...');setTimeout(()=>{location.reload()},2000);return true};btnAuto.addEventListener('click',async function(){const grid=$('#tempGrid');if(grid.length===0){logError('❌ 未找到课程列表');return}const allRowIds=grid.jqGrid('getDataIDs');const unsubmittedCount=allRowIds.filter(rowId=>{const rowData=grid.jqGrid('getRowData',rowId);return rowData.tjztmc!=='提交'}).length;if(unsubmittedCount===0){logSuccess('🎉 所有课程都已提交完成!');logSuccess('✅ 无需进行批量评价操作');return}const isCurrentSubmitted=checkSubmissionStatus();if(isCurrentSubmitted){logSuccess(`💡当前课程已提交,将批量评价其他${unsubmittedCount}个未提交课程`)}const actionText=getAutoAction()==='submit'?'提交':'保存';const evalLevel=getBatchEvalLevel();if(confirm(`确定要批量评价所有未提交的课程吗?\n\n📊未提交课程数量:${unsubmittedCount}个\n🎯评价等级:${evalLevel.name}(${evalLevel.score}分)\n⚙️操作模式:自动${actionText}\n⏱️预计耗时:${Math.ceil(unsubmittedCount*6)}秒\n\n⚠️注意:此操作将自动处理所有未提交课程,请确认后继续。`)){this.disabled=true;this.style.opacity='0.5';this.innerText='🔄 批量评价中...';try{await batchEvaluateAllCourses()}catch(error){logError(`❌批量评价过程中出错:${error.message}`)}finally{setTimeout(()=>{this.disabled=false;this.style.opacity='1';this.innerText='⚡ 批量评价'},3000)}}});const handleCourseSwitch=function(direction){this.disabled=true;this.style.opacity='0.5';const switched=switchCourse(direction);if(!switched){logWarning('❌ 切换失败,请手动选择课程')}setTimeout(()=>{this.disabled=false;this.style.opacity='1'},3000)};btnPrev.addEventListener('click',function(){handleCourseSwitch.call(this,'prev')});btnNext.addEventListener('click',function(){handleCourseSwitch.call(this,'next')});btnDonate.addEventListener('click',function(){window.open('https://dg.wdvip.top/dashang','_blank');logSuccess('💰 感谢您的支持!已打开打赏页面')});if(DEBUG_MODE){const btnDebug=createButton('btnDebug','🔧 调试',CSS_STYLES.colors.debug);addButton(btnContainer,btnDebug)}if(DEBUG_MODE){const btnDebug=btnContainer.querySelector('#btnDebug');if(btnDebug){btnDebug.addEventListener('click',function(){const doc=document.getElementsByClassName("radio-pjf");const requiredItems=document.querySelectorAll('.tr-xspj[data-sfbt="1"]');const isSubmitted=checkSubmissionStatus();const tjztElement=document.getElementById('tjzt');logDebug('=== 调试信息 ===');logDebug(`提交状态:${isSubmitted?'已提交':'未提交'}`);logDebug(`tjzt值:${tjztElement?tjztElement.value:'未找到'}`);logDebug(`总共找到${doc.length}个单选按钮`);logDebug(`总共找到${requiredItems.length}个必填评价项`);logDebug(`预期应该有${requiredItems.length*4}个单选按钮`);logDebug(`当前设置:${getAutoAction()}`);logDebug(`自动跳过已提交:${getAutoSwitchSetting()?'开启':'关闭'}`);logDebug(`评价后自动切换:${getAutoNextAfterEvalSetting()?'开启':'关闭'}`);const evalLevel=getBatchEvalLevel();logDebug(`批量评价等级:${evalLevel.name}(${evalLevel.score}分)`);logDebug(`批量评价按钮状态:${btnAuto?btnAuto.innerText:'未找到'}`);const scoreElement=document.querySelector('.xspjSum');if(scoreElement){logDebug(`评价总分:${scoreElement.textContent||scoreElement.innerText}`)}const buttons=btnContainer.querySelectorAll('button');const enabledButtons=Array.from(buttons).filter(btn=>!btn.disabled).map(btn=>btn.innerText);logDebug(`可用按钮:${enabledButtons.join(', ')}`);const grid=$('#tempGrid');if(grid.length>0){const currentRowId=grid.jqGrid('getGridParam','selrow');const allRowIds=grid.jqGrid('getDataIDs');logDebug(`jqGrid-当前选中:${currentRowId},总课程数:${allRowIds.length}`);if(currentRowId){const currentRowData=grid.jqGrid('getRowData',currentRowId);logDebug(`当前课程:${currentRowData.jxbmc}-${currentRowData.jzgmc}(${currentRowData.tjztmc})`)}}const courseRows=document.querySelectorAll('#tempGrid tr[role="row"]');logDebug(`找到${courseRows.length-1}个课程行`);if(grid.length>0){const allRowIds=grid.jqGrid('getDataIDs');const unsubmittedCount=allRowIds.filter(rowId=>{const rowData=grid.jqGrid('getRowData',rowId);return rowData.tjztmc!=='提交'}).length;const submittedCount=allRowIds.length-unsubmittedCount;logDebug(`课程状态统计:已提交${submittedCount}个,未提交${unsubmittedCount}个`)}if(doc.length>0){logDebug('--- 单选按钮状态 ---');for(let i=0;i { const notification = document.createElement('div'); applyStyles(notification, CSS_STYLES.notification.replace('top: 20px', 'top: 60px').replace('linear-gradient(135deg, #667eea 0%, #764ba2 100%)', '#28a745')); notification.innerText = '🎯 评价脚本已就绪!'; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); const doc = document.getElementsByClassName("radio-pjf"); const requiredItems = document.querySelectorAll('.tr-xspj[data-sfbt="1"]'); logSuccess('🎯 脚本加载完成'); logDebug(`📊 检测到 ${requiredItems.length} 个评价项,${doc.length} 个选项按钮`); }, 1000); })();