您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
小雅平台学习助手 📖,智能整理归纳学习资料 📚,辅助完成练习 💪,并提供便捷的查阅和修改功能 📝!
当前为
// ==UserScript== // @name 小雅答答答 // @license MIT // @version 1.30 // @description 小雅平台学习助手 📖,智能整理归纳学习资料 📚,辅助完成练习 💪,并提供便捷的查阅和修改功能 📝! // @author Yi // @match https://*.ai-augmented.com/* // @icon https://www.ai-augmented.com/static/logo3.1dbbea8f.png // @grant none // @run-at document-end // @require https://update.gf.qytechs.cn/scripts/517782/1485790/docx.js // @require https://update.gf.qytechs.cn/scripts/517783/1485791/FileSaverminjs.js // @require https://update.gf.qytechs.cn/scripts/515732/1477483/av-min.js // @homepageURL https://zygame1314.site // @namespace https://gf.qytechs.cn/users/1268039 // ==/UserScript== (function () { 'use strict'; let isActivated = false; const activationTime = localStorage.getItem('activationTime'); if (activationTime) { const currentTime = Date.now(); const elapsed = currentTime - parseInt(activationTime, 10); if (elapsed < 14400000) { isActivated = true; } else { localStorage.removeItem('isActivated'); localStorage.removeItem('activationTime'); } } const { Document, Packer, Paragraph, HeadingLevel, AlignmentType, ImageRun, TextRun } = window.docx; let autoFetchEnabled = localStorage.getItem('autoFetchEnabled') === 'true'; let autoFillEnabled = localStorage.getItem('autoFillEnabled') === 'true'; let isProcessing = false; let debounceTimer = null; function addButtons() { let panelContainer = document.createElement('div'); panelContainer.style.position = 'fixed'; panelContainer.style.top = '50px'; panelContainer.style.left = '20px'; panelContainer.style.zIndex = '9999'; panelContainer.style.width = '240px'; panelContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.95)'; panelContainer.style.backdropFilter = 'blur(10px)'; panelContainer.style.border = '1px solid rgba(230, 230, 230, 0.7)'; panelContainer.style.borderRadius = '16px'; panelContainer.style.boxShadow = '0 4px 24px rgba(0, 0, 0, 0.1)'; panelContainer.style.fontFamily = '"Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'; panelContainer.style.overflow = 'hidden'; panelContainer.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; let panelHeader = document.createElement('div'); panelHeader.style.background = 'linear-gradient(135deg, #6366F1, #4F46E5)'; panelHeader.style.padding = '15px 20px'; panelHeader.style.cursor = 'move'; panelHeader.style.color = '#fff'; panelHeader.style.fontSize = '16px'; panelHeader.style.fontWeight = 'bold'; panelHeader.style.display = 'flex'; panelHeader.style.alignItems = 'center'; panelHeader.style.justifyContent = 'space-between'; panelHeader.innerHTML = '答题助手 <span style="font-size: 18px;">⚡</span>'; let panelContent = document.createElement('div'); panelContent.style.padding = '15px'; panelContent.style.display = 'none'; panelContent.style.transition = 'all 0.3s ease'; function styleButton(btn, bgColor, hoverColor) { Object.assign(btn.style, { width: '100%', marginBottom: '10px', padding: '10px 15px', border: 'none', borderRadius: '8px', backgroundColor: bgColor, color: '#fff', fontSize: '14px', fontWeight: 'bold', cursor: 'pointer', transition: 'all 0.2s ease', textAlign: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px' }); btn.onmouseover = () => { btn.style.backgroundColor = hoverColor; btn.style.transform = 'translateY(-1px)'; }; btn.onmouseout = () => { btn.style.backgroundColor = bgColor; btn.style.transform = 'translateY(0)'; }; } let getAnswerBtn = document.createElement('button'); getAnswerBtn.innerHTML = '🕷️ 获取答案/激活脚本'; styleButton(getAnswerBtn, '#EF4444', '#DC2626'); getAnswerBtn.onclick = function () { if (isActivated) { getAndStoreAnswers(); } else { promptActivationCode(); } }; let fillAnswerBtn = document.createElement('button'); fillAnswerBtn.innerHTML = '✨ 填写答案'; styleButton(fillAnswerBtn, '#3B82F6', '#2563EB'); fillAnswerBtn.onclick = fillAnswers; let editAnswerBtn = document.createElement('button'); editAnswerBtn.innerHTML = '📝 查看/编辑答案'; styleButton(editAnswerBtn, '#FF9800', '#FB8C00'); editAnswerBtn.onclick = showAnswerEditor; let exportHomeworkBtn = document.createElement('button'); exportHomeworkBtn.innerHTML = '📄 导出作业'; styleButton(exportHomeworkBtn, '#8B5CF6', '#7C3AED'); exportHomeworkBtn.onclick = exportHomework; let isSpecialActivated = localStorage.getItem('activationCode') === 'ILOVEXIAOYA'; let makeupBtn = document.createElement('button'); makeupBtn.innerHTML = '📝 创建补交记录(慎用!)'; styleButton(makeupBtn, '#f97316', '#ea580c'); makeupBtn.onclick = enableMakeup; let submitBtn = document.createElement('button'); submitBtn.innerHTML = '📤 上传补交作业(慎用!)'; styleButton(submitBtn, '#10b981', '#059669'); submitBtn.onclick = submitHomework; let guideBtn = document.createElement('button'); guideBtn.innerHTML = '📖 使用指南'; styleButton(guideBtn, '#6366F1', '#4F46E5'); guideBtn.onclick = showTutorial; let autoFetchBtn = document.createElement('button'); autoFetchBtn.innerHTML = `${autoFetchEnabled ? '🔄' : '⭕'} 自动获取`; styleButton(autoFetchBtn, autoFetchEnabled ? '#22c55e' : '#94a3b8', autoFetchEnabled ? '#16a34a' : '#64748b'); autoFetchBtn.onclick = function () { autoFetchEnabled = !autoFetchEnabled; localStorage.setItem('autoFetchEnabled', autoFetchEnabled); autoFetchBtn.innerHTML = `${autoFetchEnabled ? '🔄' : '⭕'} 自动获取`; styleButton(autoFetchBtn, autoFetchEnabled ? '#22c55e' : '#94a3b8', autoFetchEnabled ? '#16a34a' : '#64748b'); showNotification( `已${autoFetchEnabled ? '开启' : '关闭'}自动获取功能`, { type: autoFetchEnabled ? 'success' : 'info', keywords: ['自动获取', autoFetchEnabled ? '开启' : '关闭'] } ); }; let autoFillBtn = document.createElement('button'); autoFillBtn.innerHTML = `${autoFillEnabled ? '🔄' : '⭕'} 自动填写`; styleButton(autoFillBtn, autoFillEnabled ? '#22c55e' : '#94a3b8', autoFillEnabled ? '#16a34a' : '#64748b'); autoFillBtn.onclick = function () { autoFillEnabled = !autoFillEnabled; localStorage.setItem('autoFillEnabled', autoFillEnabled); autoFillBtn.innerHTML = `${autoFillEnabled ? '🔄' : '⭕'} 自动填写`; styleButton(autoFillBtn, autoFillEnabled ? '#22c55e' : '#94a3b8', autoFillEnabled ? '#16a34a' : '#64748b'); showNotification( `已${autoFillEnabled ? '开启' : '关闭'}自动填写功能`, { type: autoFillEnabled ? 'success' : 'info', keywords: ['自动填写', autoFillEnabled ? '开启' : '关闭'] } ); }; panelContent.appendChild(autoFetchBtn); panelContent.appendChild(autoFillBtn); panelContent.appendChild(getAnswerBtn); panelContent.appendChild(fillAnswerBtn); panelContent.appendChild(editAnswerBtn); panelContent.appendChild(exportHomeworkBtn); if (isSpecialActivated) { panelContent.appendChild(makeupBtn); panelContent.appendChild(submitBtn); } panelContent.appendChild(guideBtn); let isDraggingPanel = false; panelHeader.addEventListener('click', function (e) { if (isDraggingPanel) { e.stopPropagation(); return; } if (panelContent.style.display === 'none') { panelContent.style.display = 'block'; panelContent.style.opacity = '0'; panelContent.style.transform = 'translateY(-10px)'; setTimeout(() => { panelContent.style.opacity = '1'; panelContent.style.transform = 'translateY(0)'; }, 10); } else { panelContent.style.opacity = '0'; panelContent.style.transform = 'translateY(-10px)'; setTimeout(() => { panelContent.style.display = 'none'; }, 300); } }); panelContainer.appendChild(panelHeader); panelContainer.appendChild(panelContent); document.body.appendChild(panelContainer); makePanelDraggable(panelContainer, panelHeader); function makePanelDraggable(panel, handle) { let isDragging = false; let startX = 0; let startY = 0; let offsetX = 0; let offsetY = 0; handle.addEventListener('mousedown', function (e) { isDragging = false; isDraggingPanel = false; startX = e.clientX; startY = e.clientY; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); e.preventDefault(); }); function onMouseMove(e) { let dx = e.clientX - startX; let dy = e.clientY - startY; if (Math.abs(dx) > 3 || Math.abs(dy) > 3) { isDragging = true; isDraggingPanel = true; } if (isDragging) { panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; } } function onMouseUp(e) { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); if (isDragging) { setTimeout(() => { isDraggingPanel = false; }, 50); } } } const progressBarStyles = document.createElement('style'); progressBarStyles.textContent = ` .answer-progress { position: fixed; top: 0; left: 0; width: 100%; height: 4px; background: rgba(99, 102, 241, 0.1); z-index: 10000; opacity: 0; transition: opacity 0.3s ease; } .answer-progress-bar { height: 100%; background: linear-gradient(90deg, #4f46e5, #6366f1); width: 0%; transition: width 0.3s ease; border-radius: 0 2px 2px 0; box-shadow: 0 1px 3px rgba(79, 70, 229, 0.3); } .answer-progress-text { position: fixed; top: 12px; right: 12px; font-size: 14px; color: #fff; background: #6366f1; padding: 8px 16px; border-radius: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-weight: 500; z-index: 10001; opacity: 0; transform: translateY(-10px); transition: all 0.3s ease; } `; document.head.appendChild(progressBarStyles); } function createProgressBar() { const progressContainer = document.createElement('div'); progressContainer.className = 'answer-progress'; const progressBar = document.createElement('div'); progressBar.className = 'answer-progress-bar'; const progressText = document.createElement('div'); progressText.className = 'answer-progress-text'; progressContainer.appendChild(progressBar); document.body.appendChild(progressContainer); document.body.appendChild(progressText); return { show: () => { progressContainer.style.opacity = '1'; progressText.style.opacity = '1'; progressText.style.transform = 'translateY(0)'; }, hide: () => { progressContainer.style.opacity = '0'; progressText.style.opacity = '0'; progressText.style.transform = 'translateY(-10px)'; setTimeout(() => { progressContainer.remove(); progressText.remove(); }, 300); }, update: (current, total) => { const percent = (current / total) * 100; progressBar.style.width = percent + '%'; progressText.textContent = `正在填写: ${current}/${total} 题`; } }; } addButtons(); function getNotificationContainer() { let container = document.getElementById('notification-container'); if (!container) { container = document.createElement('div'); container.id = 'notification-container'; container.style.position = 'fixed'; container.style.top = '20px'; container.style.left = '50%'; container.style.transform = 'translateX(-50%)'; container.style.zIndex = '10000'; container.style.width = '400px'; container.style.maxHeight = 'calc(100vh - 40px)'; container.style.overflowY = 'auto'; container.style.pointerEvents = 'none'; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.alignItems = 'center'; document.body.appendChild(container); } return container; } function showNotification(message, options = {}) { const { type = 'info', duration = 3000, keywords = [], } = options; const highlightColors = { success: '#ffba08', error: '#14b8a6', warning: '#8b5cf6', info: '#f472b6' }; const highlightColor = highlightColors[type] || highlightColors.info; const highlightStyle = ` color: ${highlightColor}; font-weight: bold; border-bottom: 2px solid ${highlightColor}50; transition: all 0.3s ease; border-radius: 3px; `; const highlightedMessage = keywords.reduce((msg, keyword) => { if (keyword && keyword.trim()) { const regex = new RegExp(keyword.trim(), 'g'); return msg.replace(regex, `<span style="${highlightStyle}" onmouseover="this.style.backgroundColor='${highlightColor}15'; this.style.borderBottomColor='${highlightColor}'" onmouseout="this.style.backgroundColor='transparent'; this.style.borderBottomColor='${highlightColor}50'" >${keyword}</span>`); } return msg; }, message); const notification = document.createElement('div'); notification.style.position = 'relative'; notification.style.marginBottom = '10px'; notification.style.padding = '15px 20px'; notification.style.borderRadius = '12px'; notification.style.color = '#333'; notification.style.fontSize = '16px'; notification.style.fontWeight = 'bold'; notification.style.boxShadow = '0 8px 16px rgba(0,0,0,0.08), 0 4px 8px rgba(0,0,0,0.06)'; notification.style.pointerEvents = 'auto'; notification.style.opacity = '0'; notification.style.transform = 'translateY(-20px)'; notification.style.transition = 'all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)'; notification.style.display = 'flex'; notification.style.alignItems = 'center'; notification.style.backdropFilter = 'blur(8px)'; const typeStyles = { success: { background: 'linear-gradient(145deg, rgba(104, 214, 156, 0.95), rgba(89, 186, 134, 0.95))', icon: '🎉' }, error: { background: 'linear-gradient(145deg, rgba(248, 113, 113, 0.95), rgba(220, 38, 38, 0.95))', icon: '❌' }, warning: { background: 'linear-gradient(145deg, rgba(251, 191, 36, 0.95), rgba(245, 158, 11, 0.95))', icon: '⚠️' }, info: { background: 'linear-gradient(145deg, rgba(96, 165, 250, 0.95), rgba(59, 130, 246, 0.95))', icon: 'ℹ️' } }; const currentType = typeStyles[type] || typeStyles.info; notification.style.background = currentType.background; notification.style.color = type === 'info' || type === 'success' ? '#fff' : '#000'; const progressBar = document.createElement('div'); progressBar.style.position = 'absolute'; progressBar.style.bottom = '0'; progressBar.style.left = '0'; progressBar.style.height = '4px'; progressBar.style.width = '100%'; progressBar.style.background = 'rgba(255, 255, 255, 0.3)'; progressBar.style.borderRadius = '0 0 12px 12px'; progressBar.style.transition = `width ${duration}ms cubic-bezier(0.4, 0, 0.2, 1)`; const icon = document.createElement('span'); icon.style.marginRight = '12px'; icon.style.fontSize = '20px'; icon.textContent = currentType.icon; icon.style.filter = 'saturate(1.2)'; const messageContainer = document.createElement('div'); messageContainer.innerHTML = highlightedMessage; messageContainer.style.flex = '1'; messageContainer.style.fontWeight = 'bold'; const closeBtn = document.createElement('span'); closeBtn.textContent = '×'; closeBtn.style.marginLeft = '12px'; closeBtn.style.fontSize = '24px'; closeBtn.style.cursor = 'pointer'; closeBtn.style.opacity = '0.8'; closeBtn.style.transition = 'opacity 0.2s'; closeBtn.addEventListener('mouseover', () => { closeBtn.style.opacity = '1'; }); closeBtn.addEventListener('mouseout', () => { closeBtn.style.opacity = '0.8'; }); notification.addEventListener('mouseenter', () => { notification.style.transform = 'translateY(0) scale(1.02)'; progressBar.style.transition = 'none'; }); notification.addEventListener('mouseleave', () => { notification.style.transform = 'translateY(0) scale(1)'; progressBar.style.transition = `width ${duration}ms linear`; }); notification.appendChild(icon); notification.appendChild(messageContainer); notification.appendChild(closeBtn); notification.appendChild(progressBar); const container = getNotificationContainer(); container.appendChild(notification); requestAnimationFrame(() => { notification.style.opacity = '1'; notification.style.transform = 'translateY(0)'; requestAnimationFrame(() => { progressBar.style.width = '0'; }); }); function hideNotification(notification) { notification.style.opacity = '0'; notification.style.transform = 'translateY(-20px)'; setTimeout(() => { container.removeChild(notification); if (container.children.length === 0) { document.body.removeChild(container); } }, 300); } closeBtn.addEventListener('click', (e) => { e.stopPropagation(); hideNotification(notification); }); notification.addEventListener('click', () => { hideNotification(notification); }); if (duration > 0) { setTimeout(() => { if (container.contains(notification)) { hideNotification(notification); } }, duration); } } function getDeviceFingerprint() { const screenInfo = `${window.screen.width}x${window.screen.height}x${window.screen.colorDepth}`; const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const language = navigator.language; const platform = navigator.platform; return `${screenInfo}-${timeZone}-${language}-${platform}`; } function generateActivationCode(timestamp) { const deviceInfo = getDeviceFingerprint(); const raw = deviceInfo + timestamp; let hash = 0; for (let i = 0; i < raw.length; i++) { const char = raw.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(36).toUpperCase().slice(0, 8); } function verifyActivationCode(userCode) { const currentTimestamp = Math.floor(Date.now() / (1000 * 3600 * 4)); const currentCode = generateActivationCode(currentTimestamp); const previousCode = generateActivationCode(currentTimestamp - 1); return userCode === currentCode || userCode === previousCode; } function promptActivationCode() { const modalOverlay = document.createElement('div'); modalOverlay.style.position = 'fixed'; modalOverlay.style.top = '0'; modalOverlay.style.left = '0'; modalOverlay.style.width = '100%'; modalOverlay.style.height = '100%'; modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.75)'; modalOverlay.style.zIndex = '9999'; modalOverlay.style.display = 'flex'; modalOverlay.style.alignItems = 'center'; modalOverlay.style.justifyContent = 'center'; modalOverlay.style.opacity = '0'; modalOverlay.style.transition = 'opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; modalOverlay.style.backdropFilter = 'blur(8px)'; const modalContainer = document.createElement('div'); modalContainer.style.backgroundColor = '#ffffff'; modalContainer.style.padding = '40px'; modalContainer.style.borderRadius = '20px'; modalContainer.style.boxShadow = '0 20px 50px rgba(0,0,0,0.15), 0 0 20px rgba(0,0,0,0.1)'; modalContainer.style.width = '420px'; modalContainer.style.maxWidth = '90%'; modalContainer.style.textAlign = 'center'; modalContainer.style.position = 'relative'; modalContainer.style.transform = 'scale(0.8) translateY(20px)'; modalContainer.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; modalContainer.style.border = '1px solid rgba(255, 255, 255, 0.1)'; const modalHeader = document.createElement('div'); modalHeader.style.marginBottom = '30px'; modalHeader.style.position = 'relative'; const icon = document.createElement('div'); icon.innerHTML = `<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path> </svg>`; icon.style.marginBottom = '15px'; icon.style.color = '#4CAF50'; const closeButton = document.createElement('button'); closeButton.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="18" y1="6" x2="6" y2="18"></line> <line x1="6" y1="6" x2="18" y2="18"></line> </svg>`; closeButton.style.position = 'absolute'; closeButton.style.top = '16px'; closeButton.style.right = '16px'; closeButton.style.padding = '8px'; closeButton.style.border = 'none'; closeButton.style.background = 'transparent'; closeButton.style.cursor = 'pointer'; closeButton.style.color = '#9e9e9e'; closeButton.style.transition = 'all 0.2s ease'; closeButton.style.display = 'flex'; closeButton.style.alignItems = 'center'; closeButton.style.justifyContent = 'center'; closeButton.style.width = '32px'; closeButton.style.height = '32px'; closeButton.style.borderRadius = '8px'; closeButton.addEventListener('mouseenter', () => { closeButton.style.background = '#f5f5f5'; closeButton.style.color = '#666'; closeButton.style.transform = 'rotate(90deg)'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.background = 'transparent'; closeButton.style.color = '#9e9e9e'; closeButton.style.transform = 'rotate(0deg)'; }); closeButton.addEventListener('mousedown', () => { closeButton.style.transform = 'rotate(90deg) scale(0.9)'; }); closeButton.addEventListener('mouseup', () => { closeButton.style.transform = 'rotate(90deg) scale(1)'; }); const title = document.createElement('h2'); title.textContent = '输入激活码'; title.style.fontSize = '24px'; title.style.fontWeight = '600'; title.style.color = '#333'; title.style.margin = '0 0 8px 0'; const subtitle = document.createElement('p'); subtitle.textContent = '请输入激活码以继续使用完整功能'; subtitle.style.color = '#666'; subtitle.style.fontSize = '14px'; subtitle.style.margin = '0'; const infoMessage = document.createElement('p'); infoMessage.innerHTML = '激活码免费获取,主要用于防滥用,请移步<a href="https://zygame1314.site" target="_blank" style="color: #4CAF50; text-decoration: none;">我的主页</a>'; infoMessage.style.color = '#666'; infoMessage.style.fontSize = '14px'; infoMessage.style.margin = '10px 0 0 0'; const tipMessage = document.createElement('p'); tipMessage.innerHTML = '据说只要表达对小雅的爱,就可以用上测试功能哦~'; tipMessage.style.color = '#666'; tipMessage.style.fontSize = '14px'; tipMessage.style.margin = '10px 0 0 0'; const inputContainer = document.createElement('div'); inputContainer.style.position = 'relative'; inputContainer.style.marginTop = '25px'; const input = document.createElement('input'); input.type = 'text'; input.placeholder = '请输入激活码'; input.style.width = '100%'; input.style.padding = '15px 20px'; input.style.border = '2px solid #e0e0e0'; input.style.borderRadius = '12px'; input.style.fontSize = '16px'; input.style.backgroundColor = '#f8f9fa'; input.style.transition = 'all 0.3s ease'; input.style.boxSizing = 'border-box'; input.style.outline = 'none'; input.addEventListener('focus', () => { input.style.border = '2px solid #4CAF50'; input.style.backgroundColor = '#ffffff'; input.style.boxShadow = '0 0 0 4px rgba(76, 175, 80, 0.1)'; }); input.addEventListener('blur', () => { input.style.border = '2px solid #e0e0e0'; input.style.backgroundColor = '#f8f9fa'; input.style.boxShadow = 'none'; }); const confirmButton = document.createElement('button'); confirmButton.textContent = '激活'; confirmButton.style.width = '100%'; confirmButton.style.padding = '15px'; confirmButton.style.marginTop = '20px'; confirmButton.style.border = 'none'; confirmButton.style.borderRadius = '12px'; confirmButton.style.backgroundColor = '#4CAF50'; confirmButton.style.color = '#fff'; confirmButton.style.fontSize = '16px'; confirmButton.style.fontWeight = '600'; confirmButton.style.cursor = 'pointer'; confirmButton.style.transition = 'all 0.3s ease'; confirmButton.style.transform = 'translateY(0)'; confirmButton.style.boxShadow = '0 4px 12px rgba(76, 175, 80, 0.2)'; let isLoading = false; const setLoadingState = (loading) => { isLoading = loading; if (loading) { confirmButton.innerHTML = '<span class="loading"></span>验证中...'; confirmButton.style.backgroundColor = '#45a049'; confirmButton.disabled = true; } else { confirmButton.textContent = '激活'; confirmButton.style.backgroundColor = '#4CAF50'; confirmButton.disabled = false; } }; const style = document.createElement('style'); style.textContent = ` .loading { display: inline-block; width: 20px; height: 20px; border: 3px solid rgba(255,255,255,.3); border-radius: 50%; border-top-color: #fff; animation: spin 1s ease-in-out infinite; margin-right: 8px; vertical-align: middle; } @keyframes spin { to { transform: rotate(360deg); } } `; document.head.appendChild(style); modalHeader.appendChild(icon); modalHeader.appendChild(title); modalHeader.appendChild(subtitle); modalHeader.appendChild(infoMessage); modalHeader.appendChild(tipMessage); modalContainer.appendChild(modalHeader); modalContainer.appendChild(closeButton); inputContainer.appendChild(input); modalContainer.appendChild(inputContainer); modalContainer.appendChild(confirmButton); modalOverlay.appendChild(modalContainer); document.body.appendChild(modalOverlay); requestAnimationFrame(() => { modalOverlay.style.opacity = '1'; modalContainer.style.transform = 'scale(1) translateY(0)'; }); function closeModal() { modalOverlay.style.opacity = '0'; modalContainer.style.transform = 'scale(0.8) translateY(20px)'; setTimeout(() => { document.body.removeChild(modalOverlay); document.head.removeChild(style); }, 400); } closeButton.addEventListener('click', () => { closeModal(); showNotification('请输入激活码。', { type: 'warning', keywords: ['激活码'] }); }); modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) { closeModal(); showNotification('请输入激活码。', { type: 'warning', keywords: ['激活码'] }); } }); confirmButton.addEventListener('click', () => { const userCode = input.value.trim(); if (userCode) { setLoadingState(true); setTimeout(() => { if (userCode === 'ILOVEXIAOYA') { isActivated = true; localStorage.setItem('isActivated', 'true'); localStorage.setItem('activationTime', Date.now().toString()); localStorage.setItem('activationCode', userCode); showNotification('🌟 授权成功!欢迎回来!页面将于2s后刷新以激活隐藏功能!', { type: 'success', keywords: ['超级权限', '授权成功'], duration: 5000 }); closeModal(); setTimeout(() => { location.reload(); }, 2000); getAndStoreAnswers(); } else if (verifyActivationCode(userCode)) { isActivated = true; localStorage.setItem('isActivated', 'true'); localStorage.setItem('activationTime', Date.now().toString()); showNotification('激活成功!', { type: 'success', keywords: ['激活', '成功'] }); closeModal(); getAndStoreAnswers(); } else { setLoadingState(false); input.style.border = '2px solid #ff4444'; input.style.backgroundColor = '#fff8f8'; showNotification('激活码不正确或已过期,请重试。', { type: 'error', keywords: ['激活码', '不正确', '过期'] }); input.focus(); } }, 500); } else { input.style.border = '2px solid #ff4444'; input.style.backgroundColor = '#fff8f8'; showNotification('请输入激活码。', { type: 'warning', keywords: ['激活码'] }); input.focus(); } }); input.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !isLoading) { confirmButton.click(); } }); } async function checkAssignmentStatus(groupId, nodeId) { try { const response = await fetch(`${window.location.origin}/api/jx-iresource/resource/queryCourseResources?group_id=${groupId}`, { headers: { 'authorization': `Bearer ${getToken()}`, 'content-type': 'application/json; charset=utf-8' } }); const data = await response.json(); const resources = Array.isArray(data.data) ? data.data : []; const assignment = resources.find(item => item.id === nodeId); if (assignment) { const endTime = new Date(assignment.end_time); const now = new Date(); const isExpired = now > endTime; return { isExpired, canSubmitAfterExpired: assignment.is_allow_after_submitted, endTime }; } throw new Error('未找到作业信息'); } catch (error) { console.error('检查作业状态失败:', error); return null; } } async function createRecord(paperId, groupId, token, forceCreate = false) { try { const currentUrl = window.location.href; const nodeId = getNodeIDFromUrl(currentUrl); if (!forceCreate) { const status = await checkAssignmentStatus(groupId, nodeId); if (status && status.isExpired) { showNotification(`作业已于 ${status.endTime.toLocaleString()} 截止,将不会创建答题记录,仅可查看答案。`, { type: 'warning', keywords: ['截止'] }); return null; } } const response = await fetch(`${window.location.origin}/api/jx-iresource/survey/createRecord`, { method: 'POST', headers: { 'accept': '*/*', 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=UTF-8' }, body: JSON.stringify({ paper_id: paperId, group_id: groupId }), credentials: 'include' }); const data = await response.json(); if (!data.success) { throw new Error(data.message || '创建记录失败'); } return data.data.id; } catch (error) { console.error('创建记录请求失败:', error); throw error; } } async function getAndStoreAnswers() { const taskElement = document.querySelector('#xy_app_content > div.ta-frame > div.ta_panel.ta_panel_group.ta_group > section > section > main > div > div.group-resource-header.flex_panel.hor > div.flex_align_center > div.entry_task_btn'); if (!taskElement) { showNotification('请确保在作业页面操作!', { type: 'warning', keywords: ['作业页面'] }); return; } const token = getToken(); if (!token) { showNotification('无法获取token,请确保已登录(不可用)。', { type: 'error', keywords: ['token', '登录(不可用)'] }); return; } const currentUrl = window.location.href; const node_id = getNodeIDFromUrl(currentUrl); const group_id = getGroupIDFromUrl(currentUrl); if (!node_id || !group_id) { showNotification('无法获取必要参数,请确保在正确的页面。', { type: 'error', keywords: ['参数'] }); return; } try { const resourceData = await fetch( `${window.location.origin}/api/jx-iresource/resource/queryResource?node_id=${node_id}`, { headers: { 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=utf-8' }, credentials: 'include' } ).then(res => res.json()); if (!resourceData.success) { throw new Error('获取试卷资源失败'); } const paperId = resourceData.data.resource.id; const recordId = await createRecord(paperId, group_id, token); const taskFlowData = await fetch( `${window.location.origin}/api/jx-iresource/survey/course/task/flow?node_id=${node_id}&group_id=${group_id}`, { headers: { 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=utf-8' }, credentials: 'include' } ).then(res => res.json()); if (!taskFlowData.success) { throw new Error('获取task flow数据失败'); } localStorage.setItem('recordId', recordId || ''); localStorage.setItem('groupId', group_id); localStorage.setItem('paperId', paperId); localStorage.setItem('assignmentTitle', resourceData.data.resource.title || '作业答案'); localStorage.setItem('answerData', JSON.stringify(resourceData.data.resource.questions)); showNotification('答案数据获取成功!', { type: 'success', keywords: ['答案', '获取'] }); return true; } catch (error) { console.error('获取数据失败:', error); showNotification('获取数据失败,请查看控制台。', { type: 'error', keywords: ['获取', '失败'] }); return false; } } async function fillAnswers() { const answerData = JSON.parse(localStorage.getItem('answerData')); const recordId = localStorage.getItem('recordId'); const groupId = localStorage.getItem('groupId'); const paperId = localStorage.getItem('paperId'); if (!answerData || !recordId || !groupId || !paperId) { showNotification('缺少必要数据,请先获取答案或检查作业状态。', { type: 'error', keywords: ['数据', '获取', '检查'] }); return; } const token = getToken(); if (!token) { showNotification('无法获取token。', { type: 'error', keywords: ['token'] }); return; } const progress = createProgressBar(); progress.show(); try { let completedCount = 0; const totalQuestions = answerData.length; for (const question of answerData) { await submitAnswer(question, recordId, groupId, paperId, token); completedCount++; progress.update(completedCount, totalQuestions); } progress.hide(); showNotification('答案填写完成!页面将于1s后刷新。', { type: 'success', keywords: ['答案', '填写', '刷新'] }); setTimeout(() => { location.reload(); }, 1000); } catch (error) { progress.hide(); console.error('填写答案失败:', error); showNotification('填写答案失败,请查看控制台。', { type: 'error', keywords: ['填写', '失败'] }); } } async function submitAnswer(question, recordId, groupId, paperId, token) { let answer; let extAnswer = ''; switch (question.type) { case 1: { answer = [question.answer_items.find(item => item.answer_checked === 2)?.id]; break; } case 2: { answer = question.answer_items.filter(item => item.answer_checked === 2).map(item => item.id); break; } case 4: { const fillObject = {}; question.answer_items.forEach(item => { fillObject[item.id] = item.answer; }); answer = [fillObject]; break; } case 5: { answer = [question.answer_items.find(item => item.answer_checked === 2)?.id]; break; } case 6: { answer = [question.answer_items[0].answer]; break; } case 9: { if (question.subQuestions && question.subQuestions.length > 0) { for (const subQuestion of question.subQuestions) { await submitAnswer(subQuestion, recordId, groupId, paperId, token); } } return; } case 12: { answer = question.answer_items .sort((a, b) => parseInt(a.answer) - parseInt(b.answer)) .map(item => item.id); break; } case 13: { const matchObject = {}; question.answer_items .filter(item => !item.is_target_opt && item.answer) .forEach(item => { matchObject[item.id] = item.answer; }); if (Object.keys(matchObject).length > 0) { answer = [matchObject]; } else { return; } break; } default: return; } const requestBody = { record_id: recordId, question_id: question.id, answer: answer, ext_answer: extAnswer, group_id: groupId, paper_id: paperId, is_try: 0 }; await fetch(`${window.location.origin}/api/jx-iresource/survey/answer`, { method: 'POST', headers: { 'accept': '*/*', 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=UTF-8' }, body: JSON.stringify(requestBody), credentials: 'include' }); } async function enableMakeup() { try { const taskElement = document.querySelector('#xy_app_content > div.ta-frame > div.ta_panel.ta_panel_group.ta_group > section > section > main > div > div.group-resource-header.flex_panel.hor > div.flex_align_center > div.entry_task_btn'); if (!taskElement) { showNotification('请确保在作业页面操作!', { type: 'warning', keywords: ['作业页面'] }); return; } const currentUrl = window.location.href; const node_id = getNodeIDFromUrl(currentUrl); const group_id = getGroupIDFromUrl(currentUrl); if (!node_id || !group_id) { showNotification('无法获取当前课程信息,请确保在正确的页面', { type: 'error', keywords: ['课程信息'] }); return; } const token = getToken(); if (!token) { showNotification('获取token失败,请重新登录(不可用)', { type: 'error', keywords: ['token', '登录(不可用)'] }); return; } const resourceData = await fetch( `${window.location.origin}/api/jx-iresource/resource/queryResource?node_id=${node_id}`, { headers: { 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=utf-8' } } ).then(res => res.json()); if (!resourceData.success) { throw new Error('获取试卷资源失败'); } const paperId = resourceData.data.resource.id; const recordId = await createRecord(paperId, group_id, token, true); if (recordId) { localStorage.setItem('recordId', recordId); localStorage.setItem('groupId', group_id); localStorage.setItem('paperId', paperId); localStorage.setItem('assignmentTitle', resourceData.data.resource.title || '作业答案'); localStorage.setItem('answerData', JSON.stringify(resourceData.data.resource.questions)); showNotification('已创建新的答题记录,可以开始补交了。', { type: 'success', keywords: ['补交', '记录'] }); } } catch (error) { console.error('创建失败:', error); showNotification(`创建失败,该作业不支持补交。`, { type: 'error', keywords: ['创建', '失败'] }); } } async function submitHomework() { const recordId = localStorage.getItem('recordId'); const groupId = localStorage.getItem('groupId'); if (!recordId || !groupId) { showNotification('未找到记录,请先创建记录并填写答案。', { type: 'error', keywords: ['记录', '创建记录'] }); return; } try { const token = getToken(); if (!token) { showNotification('无法获取令牌,请确保已登录(不可用)。', { type: 'error', keywords: ['令牌', '登录(不可用)'] }); return; } const response = await fetch(`${window.location.origin}/api/jx-iresource/survey/submit`, { method: 'POST', headers: { 'accept': '*/*', 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=UTF-8' }, body: JSON.stringify({ record_id: recordId, group_id: groupId }), credentials: 'include' }); const result = await response.json(); if (result.success) { showNotification('作业提交成功!1s后刷新。', { type: 'success', keywords: ['作业', '提交', '成功'] }); setTimeout(() => { location.reload(); }, 1000); } else { throw new Error(result.message || '提交失败'); } } catch (error) { console.error('提交作业失败:', error); showNotification(`提交失败,补交记录已失效。`, { type: 'error', keywords: ['提交', '失败', '记录'] }); } } function getToken() { const cookies = document.cookie.split('; '); for (let cookie of cookies) { const [name, value] = cookie.split('='); if (name.includes('prd-access-token')) { return value; } } return null; } function parseRichText(content) { try { let jsonContent = JSON.parse(content); let text = ''; jsonContent.blocks.forEach((block) => { text += block.text + '\n'; }); return text.trim(); } catch (e) { return content; } } function parseRichTextForDisplay(content) { try { let jsonContent = JSON.parse(content); let result = ''; jsonContent.blocks.forEach((block) => { if (block.type === 'atomic' && block.data && block.data.type === 'IMAGE') { let imageSrc = block.data.src; let fileIdMatch = imageSrc.match(/\/cloud\/file_access\/(\d+)/); if (fileIdMatch && fileIdMatch[1]) { let fileId = fileIdMatch[1]; let randomParam = Date.now(); let imageUrl = `${window.location.origin}/api/jx-oresource/cloud/file_access/${fileId}?random=${randomParam}`; result += `<img src="${imageUrl}" alt="图片" style="max-width: 100%; height: auto;" />`; } else { result += '[无法解析图片链接]<br>'; } } else { result += block.text.replace(/\n/g, '<br>'); } }); return result; } catch (e) { return content; } } function getNodeIDFromUrl(url) { let nodeId = null; let urlObj = new URL(url); let pathParts = urlObj.pathname.split('/').filter(part => part); nodeId = pathParts[pathParts.length - 1]; return nodeId; } function getGroupIDFromUrl(url) { const match = url.match(/mycourse\/(\d+)/); return match ? match[1] : null; } function addKeyboardShortcuts() { document.addEventListener('keydown', function (e) { if (e.ctrlKey && e.shiftKey && !e.altKey) { switch (e.key.toLowerCase()) { case 'a': e.preventDefault(); getAndStoreAnswers(); break; case 'f': e.preventDefault(); fillAnswers(); break; case 'e': e.preventDefault(); showAnswerEditor(); break; case 'q': e.preventDefault(); exportHomework(); break; default: break; } } }); } addKeyboardShortcuts(); function showTutorial() { const style = document.createElement('style'); style.textContent = ` @keyframes modalFadeIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } @keyframes floatAnimation { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } } .highlight-text { background: linear-gradient(120deg, rgba(255,223,186,0.6) 0%, rgba(255,223,186,0) 100%); padding: 0 4px; } .feature-icon { display: inline-block; width: 24px; height: 24px; margin-right: 8px; vertical-align: middle; animation: floatAnimation 3s ease-in-out infinite; } `; document.head.appendChild(style); let modalOverlay = document.createElement('div'); modalOverlay.style.position = 'fixed'; modalOverlay.style.top = '0'; modalOverlay.style.left = '0'; modalOverlay.style.width = '100%'; modalOverlay.style.height = '100%'; modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.65)'; modalOverlay.style.zIndex = '10000'; modalOverlay.style.display = 'flex'; modalOverlay.style.alignItems = 'center'; modalOverlay.style.justifyContent = 'center'; modalOverlay.style.opacity = '0'; modalOverlay.style.backdropFilter = 'blur(5px)'; modalOverlay.style.transition = 'opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; let modalContent = document.createElement('div'); modalContent.style.backgroundColor = '#fff'; modalContent.style.borderRadius = '16px'; modalContent.style.width = '90%'; modalContent.style.maxWidth = '680px'; modalContent.style.maxHeight = '85vh'; modalContent.style.overflowY = 'auto'; modalContent.style.padding = '32px'; modalContent.style.boxShadow = '0 20px 50px rgba(0, 0, 0, 0.2)'; modalContent.style.position = 'relative'; modalContent.style.transform = 'scale(0.8)'; modalContent.style.opacity = '0'; modalContent.style.animation = 'modalFadeIn 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards'; modalContent.style.background = 'linear-gradient(135deg, #fff 0%, #f8f9fa 100%)'; let closeButton = document.createElement('button'); closeButton.innerHTML = '×'; closeButton.style.position = 'absolute'; closeButton.style.top = '20px'; closeButton.style.right = '20px'; closeButton.style.fontSize = '28px'; closeButton.style.border = 'none'; closeButton.style.background = 'none'; closeButton.style.cursor = 'pointer'; closeButton.style.color = '#666'; closeButton.style.width = '40px'; closeButton.style.height = '40px'; closeButton.style.borderRadius = '50%'; closeButton.style.transition = 'all 0.3s ease'; closeButton.onmouseover = () => { closeButton.style.backgroundColor = '#f0f0f0'; closeButton.style.transform = 'rotate(90deg)'; }; closeButton.onmouseout = () => { closeButton.style.backgroundColor = 'transparent'; closeButton.style.transform = 'rotate(0deg)'; }; closeButton.onclick = () => { modalContent.style.transform = 'scale(0.8)'; modalContent.style.opacity = '0'; modalOverlay.style.opacity = '0'; setTimeout(() => document.body.removeChild(modalOverlay), 400); }; let tutorialContent = document.createElement('div'); tutorialContent.innerHTML = ` <h2 style="margin: 0 0 24px 0; color: #1a1a1a; font-weight: 700; font-size: 28px; background: linear-gradient(120deg, #2b5876 0%, #4e4376 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"> ✨ 使用指南 </h2> <p style="color: #444; line-height: 1.8; font-size: 16px;"> 欢迎使用 <span class="highlight-text" style="font-weight: 600;">小雅答答答</span> 答题助手! 这里有一些可能帮得上你的信息~ </p> <div style="margin: 32px 0; padding: 20px; background: #f8f9fa; border-radius: 12px; border-left: 4px solid #4e4376;"> <h3 style="margin: 0 0 16px 0; color: #2b5876; display: flex; align-items: center;"> <span class="feature-icon">🎯</span> 核心功能 </h3> <ol style="padding-left: 24px; color: #444; line-height: 1.8; margin: 0;"> <li><strong>获取答案</strong> - 答案在手,底气满满</li> <li><strong>填写答案</strong> - 智能填写,快速完成</li> <li><strong>编辑答案</strong> - 调整内容,应对自如</li> <li><strong>导出作业</strong> - 一键导出,便捷保存</li> <li><strong>使用指南</strong> - 随时查看,贴心帮助</li> </ol> </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 32px 0;"> <div style="padding: 24px; background: #fff; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);"> <h3 style="margin: 0 0 16px 0; color: #2b5876; display: flex; align-items: center;"> <span class="feature-icon">📝</span> 支持题型 </h3> <ul style="padding-left: 20px; color: #444; line-height: 1.8; margin: 0;"> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">单选题、多选题、填空题、判断题、简答题、数组题、排序题、匹配题</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">剩余题型可查看解析(如果有)</li> </ul> </div> <div style="padding: 24px; background: #fff; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);"> <h3 style="margin: 0 0 16px 0; color: #2b5876; display: flex; align-items: center;"> <span class="feature-icon">⌨️</span> 快捷键 </h3> <ul style="padding-left: 20px; color: #444; line-height: 1.8; margin: 0;"> <li><strong>Ctrl + Shift + A</strong>: 获取答案</li> <li><strong>Ctrl + Shift + F</strong>: 填写答案</li> <li><strong>Ctrl + Shift + E</strong>: 编辑答案</li> <li><strong>Ctrl + Shift + Q</strong>: 导出作业</li> </ul> </div> </div> <div style="margin: 32px 0; padding: 24px; background: #ffffff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08);"> <h3 style="margin: 0 0 20px 0; color: #2b5876; display: flex; align-items: center; font-size: 18px;"> <span class="feature-icon" style="margin-right: 8px;">💡</span> 使用提示 </h3> <ul style="padding-left: 20px; color: #555; line-height: 1.8; margin: 0;"> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">请确保已登录(不可用)并有权限获取题目答案</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">必须定位至作业资源才能获取到答案</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">获取答案后需进入答题页面再点击填写</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">部分题型可能需要手动检查</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">清空答案会刷新页面,请注意保存信息</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">AI答题能力有限,建议仅用于简答题,复杂题目请谨慎使用</li> <li style="border-bottom: 1px solid #edf0f2; padding: 12px 0;">若需将导出的作业导入其他题库软件,请先手动打开并随意编辑保存一次,以确保图片等内容能被正确识别</li> </ul> <div style="margin-top: 20px; padding: 16px; background: #fff2f0; border: 1px solid #ffccc7; border-radius: 8px;"> <div style="color: #ff4d4f; font-weight: bold; margin-bottom: 12px;"> ⚠️ 补交功能使用警告: </div> <ul style="padding-left: 20px; margin: 0; color: #cf1322;"> <li style="padding: 12px; margin-bottom: 8px; background: #fff6f6; border-radius: 6px; border-left: 3px solid #ff7875;"> <span style="color: #ff4d4f; font-weight: bold;">⚠️ 前提条件:</span>老师允许作业可以补交 </li> <li style="padding: 12px; margin-bottom: 8px; background: #fff6f6; border-radius: 6px;"> ❌ 大型作业和不支持的题型无法编辑! </li> <li style="padding: 12px; margin-bottom: 8px; background: #fff6f6; border-radius: 6px;"> ⚠️ 使用补交后作业将被标记为"不完全的未完成"状态,只能通过脚本编辑 </li> <li style="padding: 12px; margin-bottom: 8px; background: #fff6f6; border-radius: 6px;"> <div style="margin-bottom: 8px;">📝 已完成作业误创建补交记录的处理方案(也许你是故意的):</div> <div style="padding-left: 12px;">1. 放置不管 → 会变为"正在答题"状态</div> <div style="padding-left: 12px;">2. 重新补交 → 会增加"补交"标签</div> <div style="padding-left: 12px;">3. 覆盖了原有答案 → 简单题型可以用脚本编辑补救,复杂题型(比如附件题)就寄咯</div> </li> <li style="padding: 12px; margin-bottom: 8px; background: #fff6f6; border-radius: 6px;"> ⚠️ 仅支持通过脚本的"上传补交作业"功能提交补交数据,无法通过小雅直接提交。 </li> <li style="padding: 12px; background: linear-gradient(to right, #ffd6e7, #fff6f6); border-radius: 6px; font-weight: 500; text-align: center;"> 💝 我怎么没看到补交?那是因为你还不够爱小雅~ </li> </ul> </div> </div> <div style="margin-top: 32px; padding: 24px; background: #fff; border-radius: 12px; border: 1px dashed #4e4376;"> <h3 style="margin: 0 0 16px 0; color: #2b5876; display: flex; align-items: center;"> <span class="feature-icon">🤝</span> 需要帮助? </h3> <p style="color: #444; line-height: 1.8; margin: 0;"> 发送邮件至 <a href="mailto:[email protected]" style="color: #4e4376; text-decoration: none; border-bottom: 1px dashed #4e4376;"> [email protected] </a> 或访问 <a href="https://zygame1314.site" target="_blank" style="color: #4e4376; text-decoration: none; border-bottom: 1px dashed #4e4376;"> 我的个人主页 </a> </p> </div> <p style="margin: 32px 0 0 0; text-align: center; color: #666;">别太依赖脚本哦,多动脑才是真本事!😉</p> <p style="color: #999; font-size: 14px; text-align: center; margin-top: 10px;"> 版权 © zygame1314 保留所有权利。 </p> `; tutorialContent.style.fontSize = '16px'; tutorialContent.style.lineHeight = '1.6'; modalContent.style.scrollbarWidth = 'thin'; modalContent.style.scrollbarColor = '#4e4376 #f1f1f1'; const scrollbarStyles = ` .tutorial-modal::-webkit-scrollbar { width: 8px; } .tutorial-modal::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } .tutorial-modal::-webkit-scrollbar-thumb { background: #4e4376; border-radius: 4px; } `; style.textContent += scrollbarStyles; modalContent.classList.add('tutorial-modal'); modalContent.appendChild(closeButton); modalContent.appendChild(tutorialContent); modalOverlay.appendChild(modalContent); document.body.appendChild(modalOverlay); setTimeout(() => { modalOverlay.style.opacity = '1'; }, 10); } function getQuestionType(typeCode) { const typeMap = { 1: "单选题", 2: "多选题", 4: "填空题", 5: "判断题", 6: "简答题", 9: "数组题", 12: "排序题", 13: "匹配题" }; return typeMap[typeCode] || "未知题型"; } function createAIButton(answerInput, question) { let aiButton = document.createElement('button'); aiButton.innerHTML = '<span class="icon">🤖</span><span class="text">AI辅助</span>'; aiButton.className = 'ai-assist-btn'; aiButton.title = '使用 AI 生成答案建议'; const style = document.createElement('style'); style.textContent = ` .ai-assist-btn { position: absolute; bottom: -5px; padding: 8px 16px; background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; display: flex; align-items: center; gap: 6px; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(79, 70, 229, 0.1); } .ai-assist-btn:hover { transform: translateY(-1px); background: linear-gradient(135deg, #4338ca 0%, #4f46e5 100%); box-shadow: 0 4px 8px rgba(79, 70, 229, 0.2); } .ai-assist-btn:active { transform: translateY(1px); } .ai-assist-btn.loading { background: #6b7280; cursor: not-allowed; opacity: 0.8; } .ai-assist-btn .icon { font-size: 16px; } .ai-assist-btn.loading .icon { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } `; document.head.appendChild(style); let isLoading = false; aiButton.onclick = async () => { if (isLoading) return; try { isLoading = true; aiButton.className = 'ai-assist-btn loading'; aiButton.innerHTML = '<span class="icon">🧠</span><span class="text">生成中...</span>'; const questionType = getQuestionType(question.type); const response = await fetch(`${window.location.origin}/api/jx-oresource/assistant/chat`, { method: "POST", headers: { "content-type": "application/json", "authorization": `Bearer ${getToken()}` }, body: JSON.stringify({ ask_key: "create_answer_analysis", ask_object: { type: questionType, title: question.title, content: parseRichTextToPlainText(question.title), answer: answerInput.value, multilingual_description: ` 请按照以下要求生成【${questionType}】的答案: 1. 使用简体中文 2. 答案要清晰准确,符合题目要求 3. 适当使用专业术语 4. 分点论述,层次分明 5. 避免废话和重复内容 6. 请直接输出纯文本,不要使用特殊格式 7. 如需分点,使用数字加顿号格式 8. 根据【${questionType}】的特点组织答案结构 ` }, token: localStorage.getItem('XY_GLOBAL_CONFIG') ? JSON.parse(localStorage.getItem('XY_GLOBAL_CONFIG')).xy_ai_token : null }) }); const data = await response.json(); if (data.success) { answerInput.value = data.data; answerInput.focus(); answerInput.dispatchEvent(new Event('input', { bubbles: true })); showNotification(`AI已生成${questionType}答案建议,请检查修改`, { type: 'success', keywords: ['AI', questionType, '建议'] }); } else { throw new Error(data.message || '请求失败'); } } catch (error) { console.error('AI请求失败:', error); showNotification('AI生成失败: ' + error.message, { type: 'error', keywords: ['AI', '失败'] }); } finally { isLoading = false; aiButton.className = 'ai-assist-btn'; aiButton.innerHTML = '<span class="icon">🤖</span><span class="text">AI辅助</span>'; } }; return aiButton; } function showAnswerEditor() { let storedData = localStorage.getItem('answerData'); if (!storedData) { showNotification('未找到存储的答案数据,请先点击“获取答案”按钮。', { type: 'error', keywords: ['存储', '答案', '获取'] }); return; } let answerData = JSON.parse(storedData); let overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'; overlay.style.backdropFilter = 'blur(5px)'; overlay.style.zIndex = '9999'; overlay.style.opacity = '0'; overlay.style.transition = 'opacity 0.3s ease-in-out'; let modalContainer = document.createElement('div'); modalContainer.id = 'modal-container'; modalContainer.style.position = 'fixed'; modalContainer.style.top = '50%'; modalContainer.style.left = '50%'; modalContainer.style.transform = 'translate(-50%, -50%) scale(0.8)'; modalContainer.style.zIndex = '10000'; modalContainer.style.width = '90%'; modalContainer.style.maxWidth = '800px'; modalContainer.style.maxHeight = '1000px'; modalContainer.style.overflowY = 'auto'; modalContainer.style.backgroundColor = '#ffffff'; modalContainer.style.borderRadius = '20px'; modalContainer.style.padding = '32px'; modalContainer.style.boxShadow = '0 25px 50px -12px rgba(0, 0, 0, 0.25)'; modalContainer.style.opacity = '0'; modalContainer.style.transition = 'all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)'; let closeButton = document.createElement('button'); closeButton.innerHTML = '×'; closeButton.style.position = 'absolute'; closeButton.style.top = '20px'; closeButton.style.right = '20px'; closeButton.style.width = '36px'; closeButton.style.height = '36px'; closeButton.style.fontSize = '24px'; closeButton.style.border = 'none'; closeButton.style.background = '#f3f4f6'; closeButton.style.borderRadius = '50%'; closeButton.style.cursor = 'pointer'; closeButton.style.color = '#666'; closeButton.style.transition = 'all 0.3s ease'; closeButton.style.display = 'flex'; closeButton.style.alignItems = 'center'; closeButton.style.justifyContent = 'center'; closeButton.style.padding = '0'; closeButton.style.lineHeight = '1'; closeButton.onmouseover = () => { closeButton.style.background = '#e5e7eb'; closeButton.style.color = '#000'; closeButton.style.transform = 'rotate(90deg)'; }; closeButton.onmouseout = () => { closeButton.style.background = '#f3f4f6'; closeButton.style.color = '#666'; closeButton.style.transform = 'rotate(0deg)'; }; const closeModal = () => { overlay.style.opacity = '0'; modalContainer.style.transform = 'translate(-50%, -50%) scale(0.8)'; modalContainer.style.opacity = '0'; setTimeout(() => { document.body.removeChild(overlay); document.body.removeChild(modalContainer); }, 300); }; closeButton.onclick = closeModal; overlay.onclick = closeModal; let title = document.createElement('h2'); title.textContent = '查看/编辑答案'; title.style.margin = '0 0 28px 0'; title.style.color = '#111827'; title.style.fontSize = '24px'; title.style.fontWeight = '600'; title.style.textAlign = 'center'; let saveButton = document.createElement('button'); saveButton.textContent = '保存修改'; saveButton.style.width = '100%'; saveButton.style.marginBottom = '24px'; saveButton.style.padding = '12px 24px'; saveButton.style.fontSize = '16px'; saveButton.style.border = 'none'; saveButton.style.borderRadius = '12px'; saveButton.style.backgroundColor = '#4f46e5'; saveButton.style.color = '#ffffff'; saveButton.style.cursor = 'pointer'; saveButton.style.transition = 'all 0.3s ease'; saveButton.style.boxShadow = '0 4px 6px -1px rgba(79, 70, 229, 0.1), 0 2px 4px -1px rgba(79, 70, 229, 0.06)'; saveButton.onmouseover = () => { saveButton.style.backgroundColor = '#4338ca'; saveButton.style.transform = 'translateY(-1px)'; saveButton.style.boxShadow = '0 6px 8px -1px rgba(79, 70, 229, 0.1), 0 4px 6px -1px rgba(79, 70, 229, 0.06)'; }; saveButton.onmouseout = () => { saveButton.style.backgroundColor = '#4f46e5'; saveButton.style.transform = 'translateY(0)'; saveButton.style.boxShadow = '0 4px 6px -1px rgba(79, 70, 229, 0.1), 0 2px 4px -1px rgba(79, 70, 229, 0.06)'; }; saveButton.onclick = () => { localStorage.setItem('answerData', JSON.stringify(answerData)); showNotification('答案已保存', { type: 'success', keywords: ['答案', '保存'] }); closeModal(); }; let modalContentWrapper = document.createElement('div'); modalContentWrapper.id = 'modal-content-wrapper'; modalContentWrapper.style.display = 'flex'; modalContentWrapper.style.gap = '20px'; modalContentWrapper.style.maxHeight = 'calc(85vh - 120px)'; modalContentWrapper.style.overflowY = 'auto'; let tocContainer = document.createElement('div'); tocContainer.id = 'toc-container'; tocContainer.style.flex = '0 0 200px'; tocContainer.style.position = 'sticky'; tocContainer.style.top = '0'; tocContainer.style.alignSelf = 'flex-start'; tocContainer.style.marginBottom = '24px'; tocContainer.style.overflowY = 'auto'; tocContainer.style.maxHeight = '680px'; let tocTitle = document.createElement('h3'); tocTitle.textContent = '目录'; tocTitle.style.fontSize = '18px'; tocTitle.style.fontWeight = '600'; tocTitle.style.marginBottom = '12px'; tocTitle.style.color = '#111827'; let tocList = document.createElement('ul'); tocList.style.listStyle = 'none'; tocList.style.padding = '0'; tocList.style.margin = '0'; tocList.style.display = 'flex'; tocList.style.flexWrap = 'wrap'; tocList.style.gap = '8px'; let tocLinks = []; let questionContainers = []; answerData.forEach((_, index) => { let tocItem = document.createElement('li'); let tocLink = document.createElement('a'); tocLink.textContent = `${index + 1}`; tocLink.href = '#'; tocLink.style.display = 'flex'; tocLink.style.alignItems = 'center'; tocLink.style.justifyContent = 'center'; tocLink.style.width = '40px'; tocLink.style.height = '40px'; tocLink.style.backgroundColor = '#f3f4f6'; tocLink.style.borderRadius = '8px'; tocLink.style.color = '#1f2937'; tocLink.style.fontSize = '16px'; tocLink.style.fontWeight = '600'; tocLink.style.textDecoration = 'none'; tocLink.style.transition = 'background-color 0.2s ease'; tocLink.isActive = false; tocLink.onmouseover = () => { if (!tocLink.isActive) { tocLink.style.backgroundColor = '#e5e7eb'; } }; tocLink.onmouseout = () => { if (!tocLink.isActive) { tocLink.style.backgroundColor = '#f3f4f6'; } }; tocLink.onclick = (e) => { e.preventDefault(); let targetQuestion = document.getElementById(`question_${index}`); if (targetQuestion) { targetQuestion.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }; tocItem.appendChild(tocLink); tocList.appendChild(tocItem); tocLinks.push(tocLink); }); tocContainer.appendChild(tocTitle); tocContainer.appendChild(tocList); let content = document.createElement('div'); content.style.flex = '1'; content.style.display = 'grid'; content.style.gap = '20px'; answerData.forEach((question, index) => { let questionContainer = document.createElement('div'); questionContainer.id = `question_${index}`; questionContainer.style.padding = '24px'; questionContainer.style.backgroundColor = '#ffffff'; questionContainer.style.borderRadius = '16px'; questionContainer.style.border = '1px solid #e5e7eb'; questionContainer.style.transition = 'box-shadow 0.3s ease, margin-top 0.3s ease'; questionContainer.style.marginTop = '0'; questionContainer.style.boxShadow = '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)'; questionContainer.onmouseover = () => { questionContainer.style.boxShadow = '0 8px 16px rgba(0, 0, 0, 0.1)'; }; questionContainer.onmouseout = () => { questionContainer.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.1)'; }; let questionTitle = document.createElement('div'); questionTitle.innerHTML = `<strong>题目 ${index + 1}:</strong> ${parseRichTextForDisplay(question.title)}`; questionTitle.style.marginBottom = '16px'; questionTitle.style.color = '#111827'; questionTitle.style.fontSize = '16px'; questionTitle.style.lineHeight = '1.5'; questionContainer.appendChild(questionTitle); if ([1, 2, 5].includes(question.type)) { let optionsContainer = document.createElement('div'); optionsContainer.style.display = 'grid'; optionsContainer.style.gap = '16px'; question.answer_items.forEach((item, idx) => { let optionLabel = document.createElement('label'); optionLabel.style.display = 'flex'; optionLabel.style.alignItems = 'center'; optionLabel.style.padding = '16px'; optionLabel.style.backgroundColor = '#ffffff'; optionLabel.style.borderRadius = '12px'; optionLabel.style.cursor = 'pointer'; optionLabel.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; optionLabel.style.position = 'relative'; optionLabel.style.border = '1px solid #e5e7eb'; optionLabel.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; optionLabel.onmouseover = () => { optionLabel.style.backgroundColor = '#f8fafc'; optionLabel.style.transform = 'translateY(-1px)'; optionLabel.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.08)'; }; optionLabel.onmouseout = () => { optionLabel.style.backgroundColor = '#ffffff'; optionLabel.style.transform = 'translateY(0)'; optionLabel.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; }; let optionInput = document.createElement('input'); optionInput.type = question.type === 2 ? 'checkbox' : 'radio'; optionInput.name = `question_${question.id}`; optionInput.value = item.id; optionInput.checked = item.answer_checked === 2; optionInput.style.display = 'none'; let customCheckbox = document.createElement('span'); customCheckbox.style.width = '44px'; customCheckbox.style.height = '24px'; customCheckbox.style.backgroundColor = optionInput.checked ? '#6366f1' : '#e5e7eb'; customCheckbox.style.borderRadius = '24px'; customCheckbox.style.position = 'relative'; customCheckbox.style.marginRight = '16px'; customCheckbox.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; customCheckbox.style.boxShadow = optionInput.checked ? 'inset 0 2px 4px rgba(99, 102, 241, 0.2)' : 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; let toggleCircle = document.createElement('span'); toggleCircle.style.width = '20px'; toggleCircle.style.height = '20px'; toggleCircle.style.backgroundColor = '#ffffff'; toggleCircle.style.borderRadius = '50%'; toggleCircle.style.position = 'absolute'; toggleCircle.style.top = '2px'; toggleCircle.style.left = optionInput.checked ? '22px' : '2px'; toggleCircle.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; toggleCircle.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; toggleCircle.style.transform = optionInput.checked ? 'scale(1.1)' : 'scale(1)'; let icon = document.createElement('span'); icon.style.position = 'absolute'; icon.style.top = '50%'; icon.style.left = '50%'; icon.style.transform = 'translate(-50%, -50%)'; icon.style.transition = 'all 0.3s ease'; icon.innerHTML = optionInput.checked ? '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>' : ''; toggleCircle.appendChild(icon); customCheckbox.appendChild(toggleCircle); optionLabel.onclick = () => { optionInput.checked = !optionInput.checked; if (question.type !== 2) { let siblingInputs = optionsContainer.querySelectorAll(`input[name="question_${question.id}"]`); siblingInputs.forEach(sibling => { sibling.checked = false; let siblingToggle = sibling.nextSibling; siblingToggle.style.backgroundColor = '#e5e7eb'; siblingToggle.style.boxShadow = 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; let siblingCircle = siblingToggle.firstChild; siblingCircle.style.left = '2px'; siblingCircle.style.transform = 'scale(1)'; siblingCircle.firstChild.innerHTML = ''; }); optionInput.checked = true; } if (optionInput.checked) { customCheckbox.style.backgroundColor = '#6366f1'; customCheckbox.style.boxShadow = 'inset 0 2px 4px rgba(99, 102, 241, 0.2)'; toggleCircle.style.left = '22px'; toggleCircle.style.transform = 'scale(1.1)'; icon.innerHTML = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>'; } else { customCheckbox.style.backgroundColor = '#e5e7eb'; customCheckbox.style.boxShadow = 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; toggleCircle.style.left = '2px'; toggleCircle.style.transform = 'scale(1)'; icon.innerHTML = ''; } item.answer_checked = optionInput.checked ? 2 : 1; }; let optionText = document.createElement('span'); optionText.innerHTML = question.type === 5 ? (idx === 0 ? '正确' : '错误') : parseRichText(item.value); optionText.style.color = '#1f2937'; optionText.style.flex = '1'; optionText.style.fontSize = '15px'; optionText.style.fontWeight = '500'; optionLabel.appendChild(optionInput); optionLabel.appendChild(customCheckbox); optionLabel.appendChild(optionText); optionsContainer.appendChild(optionLabel); }); questionContainer.appendChild(questionTitle); questionContainer.appendChild(optionsContainer); } else if ([4, 6].includes(question.type)) { let inputContainer = document.createElement('div'); inputContainer.style.position = 'relative'; inputContainer.style.width = '100%'; inputContainer.style.marginTop = '8px'; inputContainer.style.paddingBottom = '40px'; let answerInput = document.createElement('textarea'); answerInput.style.width = '100%'; answerInput.style.minHeight = '160px'; answerInput.style.maxHeight = '400px'; answerInput.style.padding = '16px'; answerInput.style.paddingTop = '24px'; answerInput.style.border = '1px solid #e5e7eb'; answerInput.style.borderRadius = '12px'; answerInput.style.resize = 'vertical'; answerInput.style.fontSize = '15px'; answerInput.style.lineHeight = '1.6'; answerInput.style.color = '#1f2937'; answerInput.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; answerInput.style.backgroundColor = '#ffffff'; answerInput.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; answerInput.style.outline = 'none'; answerInput.style.display = 'block'; answerInput.style.boxSizing = 'border-box'; answerInput.style.overflow = 'auto'; let floatingLabel = document.createElement('label'); floatingLabel.textContent = '在这里输入答案'; floatingLabel.style.position = 'absolute'; floatingLabel.style.left = '16px'; floatingLabel.style.top = '2px'; floatingLabel.style.color = '#9ca3af'; floatingLabel.style.fontSize = '15px'; floatingLabel.style.fontWeight = '500'; floatingLabel.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; floatingLabel.style.pointerEvents = 'none'; floatingLabel.style.transformOrigin = 'left top'; floatingLabel.style.backgroundColor = '#ffffff'; floatingLabel.style.padding = '0 4px'; let charCount = document.createElement('div'); charCount.style.position = 'absolute'; charCount.style.right = '16px'; charCount.style.bottom = '8px'; charCount.style.fontSize = '12px'; charCount.style.color = '#9ca3af'; charCount.style.pointerEvents = 'none'; answerInput.onfocus = () => { answerInput.style.borderColor = '#6366f1'; answerInput.style.backgroundColor = '#ffffff'; answerInput.style.boxShadow = '0 4px 6px rgba(99, 102, 241, 0.1)'; floatingLabel.style.transform = 'translateY(-20px) scale(0.85)'; floatingLabel.style.color = '#6366f1'; }; answerInput.onblur = () => { answerInput.style.borderColor = '#e5e7eb'; answerInput.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; if (!answerInput.value) { floatingLabel.style.transform = 'translateY(0) scale(1)'; floatingLabel.style.color = '#9ca3af'; } }; answerInput.oninput = () => { let length = answerInput.value.length; charCount.textContent = `${length} 个字符`; question.answer_items[0].answer = answerInput.value; }; answerInput.value = question.answer_items.map(item => parseRichTextForDisplay(item.answer)).join('\n---\n'); if (answerInput.value) { floatingLabel.style.transform = 'translateY(-20px) scale(0.85)'; floatingLabel.style.color = '#6366f1'; charCount.textContent = `${answerInput.value.length} 个字符`; } let decorativeLine = document.createElement('div'); decorativeLine.style.position = 'absolute'; decorativeLine.style.left = '16px'; decorativeLine.style.right = '16px'; decorativeLine.style.bottom = '40px'; decorativeLine.style.height = '1px'; decorativeLine.style.background = 'linear-gradient(to right, #e5e7eb 50%, transparent)'; decorativeLine.style.opacity = '0.5'; inputContainer.appendChild(answerInput); inputContainer.appendChild(createAIButton(answerInput, question)); inputContainer.appendChild(floatingLabel); inputContainer.appendChild(charCount); inputContainer.appendChild(decorativeLine); questionContainer.appendChild(questionTitle); questionContainer.appendChild(inputContainer); } else if (question.type === 9) { if (question.subQuestions && question.subQuestions.length > 0) { let subQuestionsContainer = document.createElement('div'); subQuestionsContainer.style.display = 'flex'; subQuestionsContainer.style.flexDirection = 'column'; subQuestionsContainer.style.gap = '24px'; subQuestionsContainer.style.marginTop = '20px'; subQuestionsContainer.style.padding = '20px'; subQuestionsContainer.style.backgroundColor = '#f8fafc'; subQuestionsContainer.style.borderRadius = '12px'; subQuestionsContainer.style.border = '1px solid #e2e8f0'; let subQuestionTitle = document.createElement('div'); subQuestionTitle.textContent = '子题目:'; subQuestionTitle.style.fontSize = '16px'; subQuestionTitle.style.fontWeight = '600'; subQuestionTitle.style.color = '#475569'; subQuestionTitle.style.marginBottom = '16px'; subQuestionsContainer.appendChild(subQuestionTitle); question.subQuestions.forEach((subQuestion, subIndex) => { let subQuestionBox = document.createElement('div'); subQuestionBox.style.padding = '20px'; subQuestionBox.style.backgroundColor = '#ffffff'; subQuestionBox.style.borderRadius = '10px'; subQuestionBox.style.border = '1px solid #e5e7eb'; subQuestionBox.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; let subQuestionHeader = document.createElement('div'); subQuestionHeader.innerHTML = `<strong>${subIndex + 1}. </strong>${parseRichTextForDisplay(subQuestion.title)}`; subQuestionHeader.style.marginBottom = '16px'; subQuestionHeader.style.color = '#1e293b'; subQuestionHeader.style.fontSize = '15px'; subQuestionBox.appendChild(subQuestionHeader); if ([1, 2, 5].includes(subQuestion.type)) { let optionsContainer = document.createElement('div'); optionsContainer.style.display = 'grid'; optionsContainer.style.gap = '16px'; subQuestion.answer_items.forEach((item, idx) => { let optionLabel = document.createElement('label'); optionLabel.style.display = 'flex'; optionLabel.style.alignItems = 'center'; optionLabel.style.padding = '16px'; optionLabel.style.backgroundColor = '#ffffff'; optionLabel.style.borderRadius = '12px'; optionLabel.style.cursor = 'pointer'; optionLabel.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; optionLabel.style.position = 'relative'; optionLabel.style.border = '1px solid #e5e7eb'; optionLabel.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; optionLabel.onmouseover = () => { optionLabel.style.backgroundColor = '#f8fafc'; optionLabel.style.transform = 'translateY(-1px)'; optionLabel.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.08)'; }; optionLabel.onmouseout = () => { optionLabel.style.backgroundColor = '#ffffff'; optionLabel.style.transform = 'translateY(0)'; optionLabel.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; }; let optionInput = document.createElement('input'); optionInput.type = question.type === 2 ? 'checkbox' : 'radio'; optionInput.name = `question_${subQuestion.id}`; optionInput.value = item.id; optionInput.checked = item.answer_checked === 2; optionInput.style.display = 'none'; let customCheckbox = document.createElement('span'); customCheckbox.style.width = '44px'; customCheckbox.style.height = '24px'; customCheckbox.style.backgroundColor = optionInput.checked ? '#6366f1' : '#e5e7eb'; customCheckbox.style.borderRadius = '24px'; customCheckbox.style.position = 'relative'; customCheckbox.style.marginRight = '16px'; customCheckbox.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; customCheckbox.style.boxShadow = optionInput.checked ? 'inset 0 2px 4px rgba(99, 102, 241, 0.2)' : 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; let toggleCircle = document.createElement('span'); toggleCircle.style.width = '20px'; toggleCircle.style.height = '20px'; toggleCircle.style.backgroundColor = '#ffffff'; toggleCircle.style.borderRadius = '50%'; toggleCircle.style.position = 'absolute'; toggleCircle.style.top = '2px'; toggleCircle.style.left = optionInput.checked ? '22px' : '2px'; toggleCircle.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; toggleCircle.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; toggleCircle.style.transform = optionInput.checked ? 'scale(1.1)' : 'scale(1)'; let icon = document.createElement('span'); icon.style.position = 'absolute'; icon.style.top = '50%'; icon.style.left = '50%'; icon.style.transform = 'translate(-50%, -50%)'; icon.style.transition = 'all 0.3s ease'; icon.innerHTML = optionInput.checked ? '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>' : ''; toggleCircle.appendChild(icon); customCheckbox.appendChild(toggleCircle); optionLabel.onclick = () => { optionInput.checked = !optionInput.checked; if (question.type !== 2) { let siblingInputs = optionsContainer.querySelectorAll(`input[name="question_${question.id}"]`); siblingInputs.forEach(sibling => { sibling.checked = false; let siblingToggle = sibling.nextSibling; siblingToggle.style.backgroundColor = '#e5e7eb'; siblingToggle.style.boxShadow = 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; let siblingCircle = siblingToggle.firstChild; siblingCircle.style.left = '2px'; siblingCircle.style.transform = 'scale(1)'; siblingCircle.firstChild.innerHTML = ''; }); optionInput.checked = true; } if (optionInput.checked) { customCheckbox.style.backgroundColor = '#6366f1'; customCheckbox.style.boxShadow = 'inset 0 2px 4px rgba(99, 102, 241, 0.2)'; toggleCircle.style.left = '22px'; toggleCircle.style.transform = 'scale(1.1)'; icon.innerHTML = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>'; } else { customCheckbox.style.backgroundColor = '#e5e7eb'; customCheckbox.style.boxShadow = 'inset 0 2px 4px rgba(0, 0, 0, 0.05)'; toggleCircle.style.left = '2px'; toggleCircle.style.transform = 'scale(1)'; icon.innerHTML = ''; } item.answer_checked = optionInput.checked ? 2 : 1; }; let optionText = document.createElement('span'); optionText.innerHTML = question.type === 5 ? (idx === 0 ? '正确' : '错误') : parseRichText(item.value); optionText.style.color = '#1f2937'; optionText.style.flex = '1'; optionText.style.fontSize = '15px'; optionText.style.fontWeight = '500'; optionLabel.appendChild(optionInput); optionLabel.appendChild(customCheckbox); optionLabel.appendChild(optionText); optionsContainer.appendChild(optionLabel); }); subQuestionBox.appendChild(subQuestionHeader); subQuestionBox.appendChild(optionsContainer); } else if ([4, 6].includes(subQuestion.type)) { let inputContainer = document.createElement('div'); inputContainer.style.position = 'relative'; inputContainer.style.width = '100%'; inputContainer.style.marginTop = '8px'; inputContainer.style.paddingBottom = '40px'; let answerInput = document.createElement('textarea'); answerInput.style.width = '100%'; answerInput.style.minHeight = '160px'; answerInput.style.maxHeight = '400px'; answerInput.style.padding = '16px'; answerInput.style.paddingTop = '24px'; answerInput.style.border = '1px solid #e5e7eb'; answerInput.style.borderRadius = '12px'; answerInput.style.resize = 'vertical'; answerInput.style.fontSize = '15px'; answerInput.style.lineHeight = '1.6'; answerInput.style.color = '#1f2937'; answerInput.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; answerInput.style.backgroundColor = '#ffffff'; answerInput.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; answerInput.style.outline = 'none'; answerInput.style.display = 'block'; answerInput.style.boxSizing = 'border-box'; answerInput.style.overflow = 'auto'; let floatingLabel = document.createElement('label'); floatingLabel.textContent = '在这里输入答案'; floatingLabel.style.position = 'absolute'; floatingLabel.style.left = '16px'; floatingLabel.style.top = '16px'; floatingLabel.style.color = '#9ca3af'; floatingLabel.style.fontSize = '15px'; floatingLabel.style.fontWeight = '500'; floatingLabel.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; floatingLabel.style.pointerEvents = 'none'; floatingLabel.style.transformOrigin = 'left top'; floatingLabel.style.backgroundColor = '#ffffff'; floatingLabel.style.padding = '0 4px'; let charCount = document.createElement('div'); charCount.style.position = 'absolute'; charCount.style.right = '16px'; charCount.style.bottom = '8px'; charCount.style.fontSize = '12px'; charCount.style.color = '#9ca3af'; charCount.style.pointerEvents = 'none'; answerInput.onfocus = () => { answerInput.style.borderColor = '#6366f1'; answerInput.style.backgroundColor = '#ffffff'; answerInput.style.boxShadow = '0 4px 6px rgba(99, 102, 241, 0.1)'; floatingLabel.style.transform = 'translateY(-20px) scale(0.85)'; floatingLabel.style.color = '#6366f1'; }; answerInput.onblur = () => { answerInput.style.borderColor = '#e5e7eb'; answerInput.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; if (!answerInput.value) { floatingLabel.style.transform = 'translateY(0) scale(1)'; floatingLabel.style.color = '#9ca3af'; } }; answerInput.oninput = () => { let length = answerInput.value.length; charCount.textContent = `${length} 个字符`; question.answer_items[0].answer = answerInput.value; }; answerInput.value = subQuestion.answer_items.map(item => parseRichTextForDisplay(item.answer)).join('\n---\n'); if (answerInput.value) { floatingLabel.style.transform = 'translateY(-20px) scale(0.85)'; floatingLabel.style.color = '#6366f1'; charCount.textContent = `${answerInput.value.length} 个字符`; } let decorativeLine = document.createElement('div'); decorativeLine.style.position = 'absolute'; decorativeLine.style.left = '16px'; decorativeLine.style.right = '16px'; decorativeLine.style.bottom = '40px'; decorativeLine.style.height = '1px'; decorativeLine.style.background = 'linear-gradient(to right, #e5e7eb 50%, transparent)'; decorativeLine.style.opacity = '0.5'; inputContainer.appendChild(answerInput); inputContainer.appendChild(createAIButton(answerInput, question)); inputContainer.appendChild(floatingLabel); inputContainer.appendChild(charCount); inputContainer.appendChild(decorativeLine); subQuestionBox.appendChild(subQuestionHeader); subQuestionBox.appendChild(inputContainer); } if (subQuestion.description && subQuestion.description !== '{}') { let toggleDescriptionContainer = document.createElement('div'); toggleDescriptionContainer.style.cssText = ` margin: 24px 0; position: relative; `; let toggleDescriptionButton = document.createElement('button'); toggleDescriptionButton.style.cssText = ` display: flex; align-items: center; justify-content: center; font-size: 16px; color: #2563eb; background: #fff; border: 1px solid #e5e7eb; border-bottom: none; border-radius: 8px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; cursor: pointer; padding: 12px 16px; width: 100%; transition: background-color 0.3s, box-shadow 0.3s; position: relative; `; toggleDescriptionButton.innerHTML = ` <span style="display: flex; align-items: center;"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" style="display: inline-block; margin-right: 8px; transition: transform 0.3s; transform-origin: center;" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1.5 5.5l6 6 6-6H1.5z"/> </svg> <span>查看解析</span> </span> `; let descriptionContainer = document.createElement('div'); descriptionContainer.style.cssText = ` max-height: 0; overflow: hidden; transition: max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1); background-color: #f9fafb; border: 1px solid #e5e7eb; border-top: none; border-radius: 8px; border-top-left-radius: 0; border-top-right-radius: 0; margin-top: 0; `; let descriptionContent = document.createElement('div'); descriptionContent.style.cssText = ` padding: 24px; color: #111827; font-size: 16px; line-height: 1.8; `; descriptionContent.innerHTML = parseRichTextForDisplay(subQuestion.description); let isDescriptionVisible = false; toggleDescriptionButton.onclick = () => { isDescriptionVisible = !isDescriptionVisible; let svgIcon = toggleDescriptionButton.querySelector('svg'); let textLabel = toggleDescriptionButton.querySelector('span > span'); if (isDescriptionVisible) { descriptionContainer.style.maxHeight = descriptionContent.scrollHeight + 'px'; svgIcon.style.transform = 'rotate(180deg)'; textLabel.textContent = '收起解析'; toggleDescriptionButton.style.backgroundColor = '#ebf5ff'; toggleDescriptionButton.style.boxShadow = 'inset 0 3px 6px rgba(0,0,0,0.1)'; } else { descriptionContainer.style.maxHeight = '0'; svgIcon.style.transform = 'rotate(0deg)'; textLabel.textContent = '查看解析'; toggleDescriptionButton.style.backgroundColor = '#fff'; toggleDescriptionButton.style.boxShadow = 'none'; } }; descriptionContainer.appendChild(descriptionContent); toggleDescriptionContainer.appendChild(toggleDescriptionButton); toggleDescriptionContainer.appendChild(descriptionContainer); subQuestionBox.appendChild(toggleDescriptionContainer); } subQuestionsContainer.appendChild(subQuestionBox); }); questionContainer.appendChild(subQuestionsContainer); } } else if (question.type === 12) { question.answer_items.sort((a, b) => { return parseInt(a.answer) - parseInt(b.answer); }); let sortableContainer = document.createElement('div'); sortableContainer.style.display = 'flex'; sortableContainer.style.flexDirection = 'column'; sortableContainer.style.gap = '12px'; sortableContainer.style.marginTop = '16px'; question.answer_items.forEach((item, index) => { let sortableItem = document.createElement('div'); sortableItem.setAttribute('draggable', 'true'); sortableItem.dataset.id = item.id; sortableItem.dataset.index = index; sortableItem.style.display = 'flex'; sortableItem.style.alignItems = 'center'; sortableItem.style.padding = '16px'; sortableItem.style.backgroundColor = '#ffffff'; sortableItem.style.borderRadius = '12px'; sortableItem.style.border = '1px solid #e5e7eb'; sortableItem.style.cursor = 'move'; sortableItem.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; sortableItem.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; sortableItem.style.userSelect = 'none'; let orderNumber = document.createElement('div'); orderNumber.textContent = index + 1; orderNumber.style.width = '28px'; orderNumber.style.height = '28px'; orderNumber.style.borderRadius = '50%'; orderNumber.style.backgroundColor = '#6366f1'; orderNumber.style.color = '#ffffff'; orderNumber.style.display = 'flex'; orderNumber.style.alignItems = 'center'; orderNumber.style.justifyContent = 'center'; orderNumber.style.marginRight = '16px'; orderNumber.style.fontWeight = '600'; orderNumber.style.fontSize = '14px'; orderNumber.style.flexShrink = '0'; let dragHandle = document.createElement('div'); dragHandle.innerHTML = ` <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2"> <circle cx="8" cy="6" r="2" /> <circle cx="8" cy="12" r="2" /> <circle cx="8" cy="18" r="2" /> <circle cx="16" cy="6" r="2" /> <circle cx="16" cy="12" r="2" /> <circle cx="16" cy="18" r="2" /> </svg> `; dragHandle.style.marginRight = '12px'; dragHandle.style.flexShrink = '0'; dragHandle.style.opacity = '0.5'; dragHandle.style.transition = 'opacity 0.2s ease'; let itemText = document.createElement('div'); itemText.innerHTML = parseRichTextForDisplay(item.value); itemText.style.flex = '1'; itemText.style.color = '#1f2937'; itemText.style.fontSize = '15px'; itemText.style.fontWeight = '500'; sortableItem.ondragstart = (e) => { e.stopPropagation(); sortableItem.style.opacity = '0.6'; sortableItem.style.transform = 'scale(1.02)'; e.dataTransfer.setData('text/plain', sortableItem.dataset.index); sortableItem.style.backgroundColor = '#f8fafc'; }; sortableItem.ondragend = (e) => { e.stopPropagation(); sortableItem.style.opacity = '1'; sortableItem.style.transform = 'scale(1)'; sortableItem.style.backgroundColor = '#ffffff'; }; sortableItem.ondragover = (e) => { e.preventDefault(); e.stopPropagation(); sortableItem.style.transform = 'scale(1.02)'; sortableItem.style.borderColor = '#6366f1'; sortableItem.style.boxShadow = '0 4px 6px rgba(99, 102, 241, 0.1)'; }; sortableItem.ondragleave = (e) => { e.preventDefault(); e.stopPropagation(); sortableItem.style.transform = 'scale(1)'; sortableItem.style.borderColor = '#e5e7eb'; sortableItem.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; }; sortableItem.ondrop = (e) => { e.preventDefault(); e.stopPropagation(); const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); const toIndex = parseInt(sortableItem.dataset.index); if (fromIndex !== toIndex) { const items = Array.from(sortableContainer.children); const movingItem = items[fromIndex]; const targetItem = items[toIndex]; if (fromIndex < toIndex) { targetItem.parentNode.insertBefore(movingItem, targetItem.nextSibling); } else { targetItem.parentNode.insertBefore(movingItem, targetItem); } const newOrder = Array.from(sortableContainer.children).map((item, idx) => { item.querySelector('div:nth-child(2)').textContent = idx + 1; item.dataset.index = idx; return item.dataset.id; }); newOrder.forEach((id, idx) => { const answerItem = question.answer_items.find(item => item.id === id); if (answerItem) { answerItem.answer = (idx + 1).toString(); } }); } sortableItem.style.transform = 'scale(1)'; sortableItem.style.borderColor = '#e5e7eb'; sortableItem.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; }; sortableItem.onmouseover = () => { sortableItem.style.backgroundColor = '#f8fafc'; sortableItem.style.transform = 'translateY(-1px)'; sortableItem.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.08)'; dragHandle.style.opacity = '1'; }; sortableItem.onmouseout = () => { sortableItem.style.backgroundColor = '#ffffff'; sortableItem.style.transform = 'translateY(0)'; sortableItem.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.05)'; dragHandle.style.opacity = '0.5'; }; sortableItem.appendChild(dragHandle); sortableItem.appendChild(orderNumber); sortableItem.appendChild(itemText); sortableContainer.appendChild(sortableItem); }); questionContainer.appendChild(questionTitle); questionContainer.appendChild(sortableContainer); } else if (question.type === 13) { let matchingContainer = document.createElement('div'); matchingContainer.style.display = 'flex'; matchingContainer.style.flexDirection = 'column'; matchingContainer.style.gap = '16px'; matchingContainer.style.marginTop = '20px'; matchingContainer.style.padding = '16px'; matchingContainer.style.backgroundColor = '#f8fafc'; matchingContainer.style.borderRadius = '16px'; let leftItems = question.answer_items.filter(item => !item.is_target_opt); let rightItems = question.answer_items.filter(item => item.is_target_opt); let leftLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; let rightLetters = 'abcdefghijklmnopqrstuvwxyz'; let rightItemMap = {}; rightItems.forEach((rightItem, idx) => { rightItemMap[rightItem.id] = { letter: rightLetters[idx], content: rightItem.value }; }); leftItems.forEach((leftItem, idx) => { let matchItem = document.createElement('div'); matchItem.style.display = 'flex'; matchItem.style.flexDirection = 'column'; matchItem.style.padding = '20px'; matchItem.style.backgroundColor = '#ffffff'; matchItem.style.borderRadius = '12px'; matchItem.style.border = '1px solid #e2e8f0'; matchItem.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)'; matchItem.style.transition = 'all 0.3s ease'; matchItem.style.position = 'relative'; let headerContainer = document.createElement('div'); headerContainer.style.display = 'flex'; headerContainer.style.alignItems = 'flex-start'; headerContainer.style.marginBottom = '16px'; let leftLetter = leftLetters[idx]; let leftLabel = document.createElement('div'); leftLabel.textContent = leftLetter + '.'; leftLabel.style.marginRight = '12px'; leftLabel.style.fontWeight = '600'; leftLabel.style.color = '#6366f1'; leftLabel.style.fontSize = '16px'; leftLabel.style.width = '24px'; let leftContent = document.createElement('div'); leftContent.innerHTML = parseRichTextForDisplay(leftItem.value); leftContent.style.flex = '1'; leftContent.style.color = '#1e293b'; leftContent.style.fontSize = '15px'; leftContent.style.fontWeight = '500'; leftContent.style.lineHeight = '1.6'; let chipContainer = document.createElement('div'); chipContainer.style.display = 'flex'; chipContainer.style.flexWrap = 'wrap'; chipContainer.style.gap = '8px'; chipContainer.style.marginTop = '12px'; chipContainer.style.minHeight = '36px'; let selectedAnswers = []; if (leftItem.answer) { if (Array.isArray(leftItem.answer)) { selectedAnswers = leftItem.answer; } else if (typeof leftItem.answer === 'string') { selectedAnswers = leftItem.answer.split(',').filter(id => id); } else { selectedAnswers = [leftItem.answer]; } } const updateChips = () => { chipContainer.innerHTML = ''; selectedAnswers.forEach(ansId => { if (rightItemMap[ansId]) { let chip = document.createElement('div'); chip.style.display = 'flex'; chip.style.alignItems = 'center'; chip.style.padding = '6px 12px'; chip.style.backgroundColor = '#eef2ff'; chip.style.border = '1px solid #e0e7ff'; chip.style.borderRadius = '8px'; chip.style.color = '#4f46e5'; chip.style.fontSize = '14px'; chip.style.fontWeight = '500'; chip.style.cursor = 'default'; chip.style.transition = 'all 0.2s ease'; let chipText = document.createElement('span'); let rightLetter = rightItemMap[ansId].letter; chipText.innerHTML = `${rightLetter}. ${parseRichTextForDisplay(rightItemMap[ansId].content)}`; chipText.style.marginRight = '8px'; let removeIcon = document.createElement('span'); removeIcon.innerHTML = ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="18" y1="6" x2="6" y2="18"></line> <line x1="6" y1="6" x2="18" y2="18"></line> </svg> `; removeIcon.style.cursor = 'pointer'; removeIcon.style.display = 'flex'; removeIcon.style.alignItems = 'center'; removeIcon.style.padding = '2px'; removeIcon.style.borderRadius = '4px'; removeIcon.style.transition = 'all 0.2s ease'; removeIcon.onmouseover = () => { removeIcon.style.backgroundColor = '#e0e7ff'; }; removeIcon.onmouseout = () => { removeIcon.style.backgroundColor = 'transparent'; }; removeIcon.onclick = (e) => { e.stopPropagation(); chip.style.opacity = '0'; chip.style.transform = 'scale(0.8)'; setTimeout(() => { selectedAnswers = selectedAnswers.filter(id => id !== ansId); updateChips(); leftItem.answer = selectedAnswers.join(','); if (checkboxesMap[ansId]) { checkboxesMap[ansId].checked = false; } }, 200); }; chip.appendChild(chipText); chip.appendChild(removeIcon); chipContainer.appendChild(chip); } }); }; updateChips(); let dropdownButton = document.createElement('button'); dropdownButton.innerHTML = ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 8px;"> <line x1="12" y1="5" x2="12" y2="19"></line> <line x1="5" y1="12" x2="19" y2="12"></line> </svg> 添加匹配项 `; dropdownButton.style.display = 'flex'; dropdownButton.style.alignItems = 'center'; dropdownButton.style.justifyContent = 'center'; dropdownButton.style.marginTop = '16px'; dropdownButton.style.padding = '10px 16px'; dropdownButton.style.backgroundColor = '#4f46e5'; dropdownButton.style.color = '#ffffff'; dropdownButton.style.border = 'none'; dropdownButton.style.borderRadius = '8px'; dropdownButton.style.cursor = 'pointer'; dropdownButton.style.transition = 'all 0.2s ease'; dropdownButton.style.fontSize = '14px'; dropdownButton.style.fontWeight = '500'; dropdownButton.style.width = '100%'; dropdownButton.onmouseover = () => { dropdownButton.style.backgroundColor = '#4338ca'; dropdownButton.style.transform = 'translateY(-1px)'; }; dropdownButton.onmouseout = () => { dropdownButton.style.backgroundColor = '#4f46e5'; dropdownButton.style.transform = 'translateY(0)'; }; let dropdownList = document.createElement('div'); dropdownList.style.position = 'absolute'; dropdownList.style.top = '100%'; dropdownList.style.left = '0'; dropdownList.style.width = '100%'; dropdownList.style.maxHeight = '300px'; dropdownList.style.overflowY = 'auto'; dropdownList.style.border = '1px solid #e2e8f0'; dropdownList.style.borderRadius = '12px'; dropdownList.style.backgroundColor = '#ffffff'; dropdownList.style.boxShadow = '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)'; dropdownList.style.zIndex = '1000'; dropdownList.style.marginTop = '8px'; dropdownList.style.display = 'none'; dropdownList.style.opacity = '0'; dropdownList.style.transform = 'scaleY(0.9) translateY(-10px)'; dropdownList.style.transformOrigin = 'top'; dropdownList.style.transition = 'all 0.2s cubic-bezier(0.16, 1, 0.3, 1)'; dropdownList.style.scrollbarWidth = 'thin'; dropdownList.style.scrollbarColor = '#cbd5e1 #f8fafc'; let checkboxesMap = {}; rightItems.forEach((rightItem, rIdx) => { let dropdownOption = document.createElement('div'); dropdownOption.style.padding = '12px 16px'; dropdownOption.style.cursor = 'pointer'; dropdownOption.style.display = 'flex'; dropdownOption.style.alignItems = 'center'; dropdownOption.style.transition = 'all 0.2s ease'; dropdownOption.style.position = 'relative'; dropdownOption.style.borderBottom = rIdx < rightItems.length - 1 ? '1px solid #f1f5f9' : 'none'; dropdownOption.onmouseover = () => { dropdownOption.style.backgroundColor = '#f8fafc'; }; dropdownOption.onmouseout = () => { dropdownOption.style.backgroundColor = '#ffffff'; }; let rightLetter = rightLetters[rIdx]; let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.style.marginRight = '12px'; checkbox.style.width = '16px'; checkbox.style.height = '16px'; checkbox.style.accentColor = '#4f46e5'; checkbox.checked = selectedAnswers.includes(rightItem.id.toString()); checkboxesMap[rightItem.id] = checkbox; let optionContent = document.createElement('div'); optionContent.style.flex = '1'; optionContent.style.display = 'flex'; optionContent.style.alignItems = 'center'; optionContent.innerHTML = ` <span style="font-weight:600; color:#6366f1; margin-right:12px; font-size:14px;"> ${rightLetter}. </span> <span style="color:#1e293b; font-size:14px; font-weight:500;"> ${parseRichTextForDisplay(rightItem.value)} </span> `; dropdownOption.appendChild(checkbox); dropdownOption.appendChild(optionContent); dropdownOption.title = parseRichTextForDisplay(rightItem.value); checkbox.onchange = (e) => { e.stopPropagation(); if (checkbox.checked) { selectedAnswers.push(rightItem.id.toString()); } else { selectedAnswers = selectedAnswers.filter(id => id !== rightItem.id.toString()); } updateChips(); leftItem.answer = selectedAnswers.join(','); }; dropdownOption.onclick = (e) => { if (e.target !== checkbox) { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change')); } }; dropdownList.appendChild(dropdownOption); }); dropdownButton.onclick = (e) => { e.stopPropagation(); if (dropdownList.style.display === 'none') { dropdownList.style.display = 'block'; requestAnimationFrame(() => { dropdownList.style.opacity = '1'; dropdownList.style.transform = 'scaleY(1) translateY(0)'; }); } else { dropdownList.style.opacity = '0'; dropdownList.style.transform = 'scaleY(0.9) translateY(-10px)'; setTimeout(() => { dropdownList.style.display = 'none'; }, 200); } }; document.addEventListener('click', (e) => { if (!matchItem.contains(e.target)) { dropdownList.style.opacity = '0'; dropdownList.style.transform = 'scaleY(0.9) translateY(-10px)'; setTimeout(() => { dropdownList.style.display = 'none'; }, 200); } }); headerContainer.appendChild(leftLabel); headerContainer.appendChild(leftContent); matchItem.appendChild(headerContainer); matchItem.appendChild(chipContainer); matchItem.appendChild(dropdownButton); matchItem.appendChild(dropdownList); matchingContainer.appendChild(matchItem); }); questionContainer.appendChild(questionTitle); questionContainer.appendChild(matchingContainer); } else { let notSupportedMessage = document.createElement('div'); notSupportedMessage.style.padding = '20px'; notSupportedMessage.style.backgroundColor = '#fff3cd'; notSupportedMessage.style.border = '1px solid #ffeeba'; notSupportedMessage.style.borderRadius = '8px'; notSupportedMessage.style.color = '#856404'; notSupportedMessage.style.fontSize = '15px'; notSupportedMessage.style.marginTop = '16px'; notSupportedMessage.style.textAlign = 'center'; notSupportedMessage.innerHTML = ` <svg style="width: 24px; height: 24px; margin-bottom: 8px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10"></circle> <line x1="12" y1="8" x2="12" y2="12"></line> <line x1="12" y1="16" x2="12.01" y2="16"></line> </svg> <p style="margin: 0;">该题型暂不支持查看答案</p> `; questionContainer.appendChild(questionTitle); questionContainer.appendChild(notSupportedMessage); } if (question.description && question.description !== '{}') { let toggleDescriptionContainer = document.createElement('div'); toggleDescriptionContainer.style.cssText = ` margin: 24px 0; position: relative; `; let toggleDescriptionButton = document.createElement('button'); toggleDescriptionButton.style.cssText = ` display: flex; align-items: center; justify-content: center; font-size: 16px; color: #2563eb; background: #fff; border: 1px solid #e5e7eb; border-bottom: none; border-radius: 8px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; cursor: pointer; padding: 12px 16px; width: 100%; transition: background-color 0.3s, box-shadow 0.3s; position: relative; `; toggleDescriptionButton.innerHTML = ` <span style="display: flex; align-items: center;"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" style="display: inline-block; margin-right: 8px; transition: transform 0.3s; transform-origin: center;" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1.5 5.5l6 6 6-6H1.5z"/> </svg> <span>查看解析</span> </span> `; let descriptionContainer = document.createElement('div'); descriptionContainer.style.cssText = ` max-height: 0; overflow: hidden; transition: max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1); background-color: #f9fafb; border: 1px solid #e5e7eb; border-top: none; border-radius: 8px; border-top-left-radius: 0; border-top-right-radius: 0; margin-top: 0; `; let descriptionContent = document.createElement('div'); descriptionContent.style.cssText = ` padding: 24px; color: #111827; font-size: 16px; line-height: 1.8; `; descriptionContent.innerHTML = parseRichTextForDisplay(question.description); descriptionContainer.appendChild(descriptionContent); let isDescriptionVisible = false; toggleDescriptionButton.onclick = () => { isDescriptionVisible = !isDescriptionVisible; let svgIcon = toggleDescriptionButton.querySelector('svg'); let textLabel = toggleDescriptionButton.querySelector('span > span'); if (isDescriptionVisible) { descriptionContainer.style.maxHeight = descriptionContent.scrollHeight + 'px'; svgIcon.style.transform = 'rotate(180deg)'; textLabel.textContent = '收起解析'; toggleDescriptionButton.style.backgroundColor = '#ebf5ff'; toggleDescriptionButton.style.boxShadow = 'inset 0 3px 6px rgba(0,0,0,0.1)'; } else { descriptionContainer.style.maxHeight = '0'; svgIcon.style.transform = 'rotate(0deg)'; textLabel.textContent = '查看解析'; toggleDescriptionButton.style.backgroundColor = '#fff'; toggleDescriptionButton.style.boxShadow = 'none'; } }; toggleDescriptionContainer.appendChild(toggleDescriptionButton); toggleDescriptionContainer.appendChild(descriptionContainer); questionContainer.appendChild(toggleDescriptionContainer); } content.appendChild(questionContainer); questionContainers.push(questionContainer); }); modalContentWrapper.appendChild(tocContainer); modalContentWrapper.appendChild(content); modalContainer.appendChild(closeButton); modalContainer.appendChild(title); modalContainer.appendChild(saveButton); modalContainer.appendChild(modalContentWrapper); document.body.appendChild(overlay); document.body.appendChild(modalContainer); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeModal(); } }); function updateCurrentQuestionHighlight() { let currentQuestionIndex = 0; let minDifference = Infinity; questionContainers.forEach((qc, index) => { let qcRect = qc.getBoundingClientRect(); let containerRect = modalContentWrapper.getBoundingClientRect(); let difference = Math.abs(qcRect.top - containerRect.top); if (difference < minDifference) { minDifference = difference; currentQuestionIndex = index; } }); tocLinks.forEach((tocLink, idx) => { if (idx === currentQuestionIndex) { tocLink.isActive = true; tocLink.style.backgroundColor = '#6366f1'; tocLink.style.color = '#ffffff'; } else { tocLink.isActive = false; tocLink.style.backgroundColor = '#f3f4f6'; tocLink.style.color = '#1f2937'; } }); } modalContentWrapper.addEventListener('scroll', updateCurrentQuestionHighlight); requestAnimationFrame(() => { overlay.style.opacity = '1'; modalContainer.style.transform = 'translate(-50%, -50%) scale(1)'; modalContainer.style.opacity = '1'; updateCurrentQuestionHighlight(); }); } async function exportHomework() { console.log('exportHomework function called'); let storedData = localStorage.getItem('answerData'); if (!storedData) { console.error('未找到存储的答案数据,请先获取并存储答案。'); showNotification('未找到存储的答案数据,请先点击“获取答案”按钮。', { type: 'error', keywords: ['存储', '答案', '获取'] }); return; } const answerData = JSON.parse(storedData); let assignmentTitle = localStorage.getItem('assignmentTitle') || '作业答案'; try { const docContent = []; showNotification('已开始导出,请稍后……', { type: 'info', keywords: ['导出', '开始'] }); docContent.push( new Paragraph({ text: assignmentTitle, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, spacing: { after: 400 }, }), new Paragraph({ text: `导出时间:${new Date().toLocaleString()}`, alignment: AlignmentType.CENTER, spacing: { after: 400 }, }) ); for (let index = 0; index < answerData.length; index++) { const question = answerData[index]; const questionNumber = `${index + 1}、`; const titleRuns = await parseRichTextContent(question.title); const titleParagraph = new Paragraph({ children: [ new TextRun({ text: questionNumber, bold: true, }), ...titleRuns, ], }); docContent.push(titleParagraph); switch (question.type) { case 1: case 2: { const options = question.answer_items.map((item, idx) => { const optionLetter = String.fromCharCode(65 + idx); return { letter: optionLetter, content: item.value, }; }); for (const option of options) { const optionRuns = await parseRichTextContent(option.content); const optionParagraph = new Paragraph({ children: [ new TextRun({ text: `${option.letter}. `, bold: true, }), ...optionRuns, ], }); docContent.push(optionParagraph); } const correctOptions = question.answer_items .map((item, idx) => item.answer_checked === 2 ? String.fromCharCode(65 + idx) : null) .filter(item => item !== null) .join(''); docContent.push( new Paragraph({ text: `答案:${correctOptions}`, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } case 5: { const isCorrect = question.answer_items.some(item => item.answer_checked === 2 && (item.value === '正确' || item.value.toLowerCase() === 'true')); const answerText = isCorrect ? '对' : '错'; docContent.push( new Paragraph({ text: `答案:${answerText}`, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } case 4: { const blankCount = question.answer_items.length; let blanks = ''; for (let i = 0; i < blankCount; i++) { blanks += '(____)'; } docContent.push( new Paragraph({ text: blanks, spacing: { before: 100, after: 100 }, }) ); const answers = question.answer_items.map(item => parseRichTextToPlainText(item.answer)).join('|'); docContent.push( new Paragraph({ text: `答案:${answers}`, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } case 6: { const answers = question.answer_items.map(item => parseRichTextToPlainText(item.answer)).join(';'); docContent.push( new Paragraph({ text: `答案:${answers}`, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } case 9: { if (question.subQuestions && question.subQuestions.length > 0) { for (let subIndex = 0; subIndex < question.subQuestions.length; subIndex++) { const subQuestion = question.subQuestions[subIndex]; const subQuestionNumber = `${index + 1}.${subIndex + 1}、`; const subTitleRuns = await parseRichTextContent(subQuestion.title); docContent.push( new Paragraph({ children: [ new TextRun({ text: subQuestionNumber, bold: true, }), ...subTitleRuns ], spacing: { before: 200 } }) ); switch (subQuestion.type) { case 1: case 2: { const options = subQuestion.answer_items.map((item, idx) => { const optionLetter = String.fromCharCode(65 + idx); return { letter: optionLetter, content: item.value }; }); for (const option of options) { const optionRuns = await parseRichTextContent(option.content); docContent.push( new Paragraph({ children: [ new TextRun({ text: `${option.letter}. `, bold: true }), ...optionRuns ] }) ); } const correctOptions = subQuestion.answer_items .map((item, idx) => item.answer_checked === 2 ? String.fromCharCode(65 + idx) : null) .filter(item => item !== null) .join(''); docContent.push( new Paragraph({ text: `答案:${correctOptions}`, spacing: { before: 100, after: 100 } }) ); break; } case 4: { const blankCount = subQuestion.answer_items.length; let blanks = ''; for (let i = 0; i < blankCount; i++) { blanks += '(____)'; } docContent.push( new Paragraph({ text: blanks, spacing: { before: 100, after: 100 } }) ); const answers = subQuestion.answer_items .map(item => parseRichTextToPlainText(item.answer)) .join('|'); docContent.push( new Paragraph({ text: `答案:${answers}`, spacing: { before: 100, after: 100 } }) ); break; } case 5: { const isCorrect = subQuestion.answer_items .some(item => item.answer_checked === 2 && (item.value === '正确' || item.value.toLowerCase() === 'true')); const answerText = isCorrect ? '对' : '错'; docContent.push( new Paragraph({ text: `答案:${answerText}`, spacing: { before: 100, after: 100 } }) ); break; } case 6: { const answers = subQuestion.answer_items .map(item => parseRichTextToPlainText(item.answer)) .join(';'); docContent.push( new Paragraph({ text: `答案:${answers}`, spacing: { before: 100, after: 100 } }) ); break; } } if (subQuestion.description && subQuestion.description !== '{}') { const descriptionRuns = await parseRichTextContent(subQuestion.description); docContent.push( new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true }), ...descriptionRuns ], spacing: { before: 100, after: 100 } }) ); } docContent.push( new Paragraph({ text: '', spacing: { after: 200 } }) ); } } break; } case 12: { const options = question.answer_items.map((item, idx) => { const optionLetter = String.fromCharCode(65 + idx); return { letter: optionLetter, content: item.value, originalIndex: idx, }; }); for (const option of options) { const optionRuns = await parseRichTextContent(option.content); const optionParagraph = new Paragraph({ children: [ new TextRun({ text: `${option.letter}. `, bold: true, }), ...optionRuns, ], }); docContent.push(optionParagraph); } const sortedItems = question.answer_items.slice().sort((a, b) => parseInt(a.answer) - parseInt(b.answer)); const answerLetters = sortedItems.map(item => { const originalIndex = question.answer_items.indexOf(item); return String.fromCharCode(65 + originalIndex); }).join(''); docContent.push( new Paragraph({ text: `答案:${answerLetters}`, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } case 13: { const leftItems = question.answer_items.filter(item => !item.is_target_opt); const rightItems = question.answer_items.filter(item => item.is_target_opt); docContent.push(new Paragraph({ text: "左侧选项:" })); leftItems.forEach((leftItem, index) => { const leftContent = parseRichTextToPlainText(leftItem.value); docContent.push(new Paragraph({ text: `左${index + 1}:${leftContent}`, })); }); docContent.push(new Paragraph({ text: "右侧选项:" })); rightItems.forEach((rightItem, index) => { const rightContent = parseRichTextToPlainText(rightItem.value); docContent.push(new Paragraph({ text: `右${index + 1}:${rightContent}`, })); }); const answerText = '答案:' + leftItems.map((leftItem, leftIndex) => { const leftOptionNumber = `左${leftIndex + 1}`; const matchedRightIds = leftItem.answer ? leftItem.answer.toString().split(',') : []; const matchedRightNumbers = matchedRightIds.map((id) => { const rightIndex = rightItems.findIndex(item => item.id === id); return rightIndex >= 0 ? `右${rightIndex + 1}` : ''; }).join('、'); return `${leftOptionNumber} - ${matchedRightNumbers}`; }).join('|'); docContent.push( new Paragraph({ text: answerText, spacing: { before: 100, after: 100 }, }) ); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } default: { docContent.push(new Paragraph({ text: "该题型暂不支持查看答案。", spacing: { before: 100, after: 100 }, })); if (question.description && question.description !== '{}') { const descriptionRuns = await parseRichTextContent(question.description); const descriptionParagraph = new Paragraph({ children: [ new TextRun({ text: '解析:', bold: true, }), ...descriptionRuns, ], spacing: { before: 100, after: 100 }, }); docContent.push(descriptionParagraph); } break; } } docContent.push(new Paragraph({ text: "", spacing: { after: 200 } })); } const doc = new Document({ creator: "小雅答答答", description: `导出的作业答案 - ${assignmentTitle}`, title: assignmentTitle, numbering: { config: [ { reference: "default", levels: [ { level: 0, format: "decimal", text: "%1.", alignment: AlignmentType.START, }, ], }, ], }, styles: { default: { document: { run: { font: "Microsoft YaHei", size: 24, }, }, }, paragraphStyles: [ { id: "Normal", name: "Normal", run: { font: "Microsoft YaHei", size: 24, }, paragraph: { spacing: { line: 360, before: 0, after: 0 }, }, }, { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true, run: { font: "Microsoft YaHei", size: 32, bold: true, }, paragraph: { spacing: { before: 240, after: 120 }, }, }, ], }, sections: [ { properties: { page: { margin: { top: 720, right: 720, bottom: 720, left: 720 }, size: { width: 11906, height: 16838, }, }, }, children: docContent, }, ], compatibility: { doNotExpandShiftReturn: true, useWord2013TrackBottomHyphenation: true, compatibilityMode: 15, useFELayout: true, }, settings: { compatibility: { useFELayout: true, useNormalStyleForList: true, doNotUseIndentAsNumberingTabStop: true, balanceSingleByteDoubleByteWidth: true } } }); Packer.toBlob(doc).then((blob) => { let safeTitle = assignmentTitle.replace(/[\\/:*?"<>|]/g, '_'); window.saveAs(blob, `${safeTitle}.docx`); showNotification('已成功导出,如需导入其他题库,请手动编辑保存一次以确保被准确识别。', { type: 'success', keywords: ['导出', '成功', '题库'] }); }).catch((error) => { console.error('导出失败:', error); showNotification('导出失败,请查看控制台日志。', { type: 'error', keywords: ['导出', '失败', '日志'] }); }); } catch (error) { console.error('An error occurred in exportHomework:', error); showNotification('导出失败,请查看控制台日志。', { type: 'error', keywords: ['导出', '失败', '日志'] }); } } async function parseRichTextContent(content) { let result = []; try { let jsonContent = JSON.parse(content); for (const block of jsonContent.blocks) { if (block.type === 'atomic' && block.data && block.data.type === 'IMAGE') { let imageSrc = block.data.src; let fileIdMatch = imageSrc.match(/\/cloud\/file_access\/(\d+)/); if (fileIdMatch && fileIdMatch[1]) { let fileId = fileIdMatch[1]; let randomParam = Date.now(); let imageUrl = `${window.location.origin}/api/jx-oresource/cloud/file_access/${fileId}?random=${randomParam}`; const imageData = await fetchImageData(imageUrl); if (imageData) { const imageSize = await getImageSize(imageData); if (imageSize) { let { width, height } = imageSize; const maxWidth = 500; if (width > maxWidth) { const ratio = maxWidth / width; width = maxWidth; height = height * ratio; } result.push( new ImageRun({ data: imageData, transformation: { width: width, height: height, }, }) ); } else { result.push(new TextRun('[无法加载图片]')); } } else { result.push(new TextRun('[无法加载图片]')); } } else { result.push(new TextRun('[无法解析图片链接]')); } } else if (block.text) { result.push(new TextRun({ text: block.text, font: "Microsoft YaHei", eastAsia: "Microsoft YaHei" })); } } } catch (e) { const sanitizedContent = content.replace(/[\x00-\x1F\x7F\u200B-\u200D\uFEFF]/g, ''); if (sanitizedContent) { result.push(new TextRun({ text: sanitizedContent, font: "Microsoft YaHei" })); } } return result; } function parseRichTextToPlainText(content) { try { let jsonContent = JSON.parse(content); let result = ''; jsonContent.blocks.forEach((block) => { result += block.text + '\n'; }); return result.trim(); } catch (e) { return content; } } async function getImageSize(imageData) { return new Promise((resolve, reject) => { const blob = new Blob([imageData]); const url = URL.createObjectURL(blob); const img = new Image(); img.onload = function () { const width = img.width; const height = img.height; URL.revokeObjectURL(url); resolve({ width, height }); }; img.onerror = function () { URL.revokeObjectURL(url); reject(new Error('Cannot load image')); }; img.src = url; }); } async function fetchImageData(url) { try { const response = await fetch(url, { method: 'GET' }); if (response.ok) { const blob = await response.blob(); return await blob.arrayBuffer(); } else { console.error('获取图片失败:', response.statusText); return null; } } catch (error) { console.error('fetchImageData Error:', error); return null; } } function checkAndExecuteAuto() { if (isProcessing) { return; } if (debounceTimer) { clearTimeout(debounceTimer); } debounceTimer = setTimeout(async () => { const taskElement = document.querySelector('#xy_app_content > div.ta-frame > div.ta_panel.ta_panel_group.ta_group > section > section > main > div > div.group-resource-header.flex_panel.hor > div.flex_align_center > div.entry_task_btn'); if (taskElement && autoFetchEnabled) { if (!isActivated) { showNotification('请先激活后再使用自动功能', { type: 'warning', keywords: ['激活', '自动功能'] }); debounceTimer = null; return; } try { isProcessing = true; showNotification('正在自动获取答案...', { type: 'info', keywords: ['自动', '获取', '答案'] }); await getAndStoreAnswers(); if (autoFillEnabled) { await new Promise(resolve => setTimeout(resolve, 1000)); await fillAnswers(); } } catch (error) { console.error('自动执行出错:', error); } finally { isProcessing = false; debounceTimer = null; } } else { debounceTimer = null; } }, 500); } function detectPageChange() { let lastUrl = location.href; const observer = new MutationObserver(() => { if (location.href !== lastUrl) { lastUrl = location.href; isProcessing = false; if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; } setTimeout(() => { checkAndExecuteAuto(); }, 1000); } }); observer.observe(document, { subtree: true, childList: true }); checkAndExecuteAuto(); } detectPageChange(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址