document.querySelector('.file-input-button').addEventListener('click', function() { document.getElementById('file').click(); }); document.getElementById('file').addEventListener('change', function(e) { const fileName = e.target.files[0] ? e.target.files[0].name : '未选择文件'; document.getElementById('file-name').textContent = fileName; }); document.getElementById('upload-form').addEventListener('submit', async (e) => { e.preventDefault(); handleSubmission(); }); document.getElementById('verify-submit-button').addEventListener('click', async () => { await validateCodeAndSubmit(); }); async function handleSubmission() { const assignment = document.getElementById('assignment').value; const file = document.getElementById('file').files[0]; const statusDiv = document.getElementById('status'); if (!assignment || !file) { alert('请选择作业,并选择一个文件。'); return; } setLoadingState(true); try { const recordResponse = await fetch('/record-submission', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ assignment, filename: file.name }) }); if (recordResponse.status === 401) { setLoadingState(false); document.getElementById('verification-container').style.display = 'block'; const emailResponse = await fetch('/generate-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ assignment }) }); if (emailResponse.ok) { alert('作业已提交过,请输入验证码提交'); } else { const errorData = await emailResponse.json(); throw new Error(`发送验证码失败: ${errorData.error}`); } } else if (!recordResponse.ok) { const errorRecordData = await recordResponse.json(); throw new Error(`记录提交信息失败: ${errorRecordData.error}`); } else { const data = await recordResponse.json(); await uploadFile(data.upload_url, file, statusDiv); alert('文件上传并记录成功'); } } catch (error) { console.error('错误:', error); statusDiv.textContent = `错误: ${error.message}`; alert('发生错误: ' + error.message); } finally { setLoadingState(false); } } async function validateCodeAndSubmit() { const assignment = document.getElementById('assignment').value; const file = document.getElementById('file').files[0]; const code = document.getElementById('verification-code').value; const statusDiv = document.getElementById('status'); setLoadingState(true, true); try { const validateResponse = await fetch('/validate-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ assignment, code }) }); if (validateResponse.ok) { const { upload_url } = await validateResponse.json(); await uploadFile(upload_url, file, statusDiv); alert('文件上传并记录成功'); document.getElementById('verification-container').style.display = 'none'; } else { const errorData = await validateResponse.json(); throw new Error(`验证码错误: ${errorData.error}`); } } catch (error) { console.error('错误:', error); statusDiv.textContent = `错误: ${error.message}`; alert('发生错误: ' + error.message); } finally { setLoadingState(false, true); } } async function uploadFile(upload_url, file, statusDiv) { const progressContainer = document.getElementById('progress-container'); const progressBar = document.getElementById('progress-bar'); const progressPercentage = document.getElementById('progress-percentage'); const uploadSpeed = document.getElementById('upload-speed'); const uploadSize = document.getElementById('upload-size'); const uploadTime = document.getElementById('upload-time'); statusDiv.textContent = '正在上传文件...'; const totalSize = file.size; let uploadedSize = 0; let startTime = Date.now(); progressContainer.style.display = 'block'; const updateProgress = (additionalProgress = 0) => { uploadedSize += additionalProgress; const percentComplete = (uploadedSize / totalSize) * 100; const elapsedTime = (Date.now() - startTime) / 1000; const speed = uploadedSize / elapsedTime; const remainingSize = totalSize - uploadedSize; const estimatedRemainingTime = speed > 0 ? remainingSize / speed : 0; progressBar.style.width = percentComplete + '%'; progressPercentage.textContent = percentComplete.toFixed(2) + '%'; uploadSpeed.textContent = `速度: ${formatSize(speed)}/s`; uploadSize.textContent = `${formatSize(uploadedSize)} / ${formatSize(totalSize)}`; uploadTime.textContent = `剩余时间: ${formatTime(estimatedRemainingTime)}`; }; const progressInterval = setInterval(() => { if (uploadedSize < totalSize) { updateProgress(totalSize / 100); } }, 200); const uploadResponse = await fetch(upload_url, { method: 'PUT', body: file, headers: { 'Content-Type': 'application/octet-stream' } }); clearInterval(progressInterval); if (!uploadResponse.ok) { throw new Error(`上传失败: ${uploadResponse.statusText}`); } updateProgress(totalSize - uploadedSize); } function setLoadingState(isLoading, isVerify = false) { const submitButton = document.querySelector('button[type="submit"]'); const verifyButton = document.getElementById('verify-submit-button'); const container = isVerify ? verifyButton.parentElement : submitButton.parentElement; const loadingIndicator = container.querySelector('.loading-indicator') || document.createElement('div'); loadingIndicator.className = 'loading-indicator'; loadingIndicator.innerHTML = '加载中...'; loadingIndicator.style.display = isLoading ? 'block' : 'none'; if (!loadingIndicator.parentElement) { container.appendChild(loadingIndicator); } if (isVerify) { verifyButton.disabled = isLoading; } else { submitButton.disabled = isLoading; } } function formatSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function formatTime(seconds) { if (isNaN(seconds) || !isFinite(seconds)) { return '计算中'; } if (seconds < 60) return Math.round(seconds) + ' 秒'; const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.round(seconds % 60); return `${minutes} 分 ${remainingSeconds} 秒`; } function previewTable(apiUrl, title) { fetch(apiUrl) .then(response => response.json()) .then(data => { const newWindow = window.open('', '_blank'); newWindow.document.write('' + title + ''); newWindow.document.write('

' + title + '

'); const table = newWindow.document.createElement('table'); if (data.length > 0) { let headers; if (title === '预览统计表格') { headers = ['时间', '提交的文件', '姓名', '学号', '作业']; } else { headers = ['学号', '姓名', '作业', '提交情况', '提交的文件']; } const thead = newWindow.document.createElement('thead'); const tr = newWindow.document.createElement('tr'); headers.forEach((header, index) => { const th = newWindow.document.createElement('th'); th.textContent = header; // 设置列宽 if (title === '预览统计表格') { if (index === 0) th.style.width = '20%'; // 时间 else if (index === 1) th.style.width = '30%'; // 提交的文件 else if (index === 2) th.style.width = '15%'; // 姓名 else if (index === 3) th.style.width = '15%'; // 学号 else if (index === 4) th.style.width = '20%'; // 作业 } else { if (index === 0) th.style.width = '15%'; // 学号 else if (index === 1) th.style.width = '15%'; // 姓名 else if (index === 2) th.style.width = '20%'; // 作业 else if (index === 3) th.style.width = '15%'; // 提交情况 else if (index === 4) th.style.width = '35%'; // 提交的文件 } tr.appendChild(th); }); thead.appendChild(tr); table.appendChild(thead); } const tbody = newWindow.document.createElement('tbody'); data.forEach(row => { const tr = newWindow.document.createElement('tr'); if (title === '预览统计表格') { ['时间', '提交的文件', '姓名', '学号', '作业'].forEach(key => { const td = newWindow.document.createElement('td'); td.textContent = row[key] || ''; tr.appendChild(td); }); } else { ['学号', '姓名', '作业', '提交情况', '提交的文件'].forEach(key => { const td = newWindow.document.createElement('td'); td.textContent = row[key] || ''; tr.appendChild(td); }); } tbody.appendChild(tr); }); table.appendChild(tbody); newWindow.document.body.appendChild(table); newWindow.document.write(''); newWindow.document.close(); }) .catch(error => console.error('Error fetching data:', error)); }