- 新增 parse_diff_blocks 系列函数,将 runtimeinfo 解析为结构化的测试点对比块,支持 simple 表格和 diff -y 两种格式 - 在判题结果页插入"输出对比"区块,逐测试点并列展示期望输出与用户输出,差异行高亮 - 补充 diff-section 相关样式,移动端切换为单列布局
730 lines
27 KiB
PHP
730 lines
27 KiB
PHP
<?php $show_title=$id." - $MSG_ERROR_INFO - $OJ_NAME"; ?>
|
||
<?php include("template/$OJ_TEMPLATE/header.php");?>
|
||
|
||
<script src="include/vditor-adapter.js"></script>
|
||
<script src="template/<?php echo $OJ_TEMPLATE?>/js/textFit.min.js"></script>
|
||
<style>
|
||
.single-subtask {
|
||
box-shadow: none !important;
|
||
}
|
||
.single-subtask > .title {
|
||
display: none;
|
||
}
|
||
.single-subtask > .content {
|
||
padding: 0 !important;
|
||
}
|
||
.accordion > .content > .accordion {
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
}
|
||
.accordion > .content > .accordion > .content {
|
||
margin-top: 0;
|
||
margin-bottom: 14px;
|
||
}
|
||
.accordion > .content > .accordion > .content > :last-child {
|
||
margin-bottom: -10px !important;
|
||
}
|
||
|
||
/* === reinfo 友好化样式 === */
|
||
#result-banner {
|
||
margin-bottom: 18px;
|
||
border-left: 5px solid;
|
||
}
|
||
#result-banner.green { border-left-color: #21ba45; }
|
||
#result-banner.red { border-left-color: #db2828; }
|
||
#result-banner.yellow{ border-left-color: #fbbd08; }
|
||
#result-banner.blue { border-left-color: #2185d0; }
|
||
#result-banner.grey { border-left-color: #767676; }
|
||
|
||
#result-banner h2.ui.header {
|
||
margin: 0;
|
||
}
|
||
#result-banner .verdict-stats {
|
||
margin-top: 6px;
|
||
font-size: 0.95em;
|
||
color: rgba(0,0,0,0.6);
|
||
}
|
||
#result-banner .verdict-stats .stat {
|
||
margin-right: 16px;
|
||
}
|
||
#result-banner .verdict-stats i.icon {
|
||
margin-right: 4px;
|
||
}
|
||
|
||
#verdict-tip {
|
||
margin-bottom: 18px;
|
||
font-size: 1.05em;
|
||
padding: 8px 12px;
|
||
background: rgba(33,150,243,0.08);
|
||
border-radius: 6px;
|
||
border-left: 3px solid #2185d0;
|
||
}
|
||
|
||
#raw-info-accordion {
|
||
margin-bottom: 18px;
|
||
}
|
||
#raw-info-accordion > .content {
|
||
padding: 8px 14px;
|
||
}
|
||
#raw-info-accordion pre#errtxt {
|
||
margin: 0;
|
||
padding: 12px;
|
||
background: #f7f7f9;
|
||
border-radius: 4px;
|
||
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
|
||
font-size: 0.9em;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
#raw-info-accordion .title .dedup-label {
|
||
margin-left: 8px;
|
||
}
|
||
#raw-info-accordion .empty-notice {
|
||
color: #999;
|
||
font-style: italic;
|
||
}
|
||
|
||
.quick-actions {
|
||
text-align: center;
|
||
margin-top: 24px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.quick-actions .ui.buttons {
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* AI 点评区 */
|
||
#llm-section {
|
||
margin-top: 20px;
|
||
margin-bottom: 18px;
|
||
}
|
||
#llm-section .ui.header {
|
||
margin-top: 0;
|
||
}
|
||
#llm-section .llm-section-header {
|
||
transition: background-color 0.15s;
|
||
padding: 4px 8px;
|
||
margin-left: -8px;
|
||
margin-right: -8px;
|
||
border-radius: 4px;
|
||
}
|
||
#llm-section .llm-section-header:hover {
|
||
background-color: rgba(33,150,243,0.06);
|
||
}
|
||
#llm-section .llm-toggle-icon {
|
||
transition: transform 0.2s;
|
||
color: #888;
|
||
}
|
||
#llm-review-result {
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* 错误解释列表(由 JS explain() 填充) - 已弃用,由 AI 点评替代 */
|
||
|
||
/* 自动刷新状态 */
|
||
i.icon.spinning {
|
||
animation: spin 1.2s linear infinite;
|
||
}
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
#auto-refresh-status {
|
||
color: #2185d0;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Diff 对比样式 */
|
||
#diff-section {
|
||
margin-bottom: 18px;
|
||
}
|
||
.diff-card {
|
||
margin-bottom: 14px;
|
||
border-left: 4px solid #fbbd08;
|
||
padding: 12px 16px;
|
||
}
|
||
.diff-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 10px;
|
||
padding-bottom: 8px;
|
||
border-bottom: 1px solid #eee;
|
||
font-weight: 600;
|
||
color: #555;
|
||
}
|
||
.diff-name {
|
||
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
|
||
font-size: 0.95em;
|
||
}
|
||
.diff-table {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 12px;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.diff-table { grid-template-columns: 1fr; }
|
||
}
|
||
.diff-side {
|
||
background: #fafafa;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
border: 1px solid #e0e0e0;
|
||
}
|
||
.diff-side-header {
|
||
padding: 6px 10px;
|
||
font-size: 0.85em;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
.diff-expected .diff-side-header {
|
||
background: #21ba45;
|
||
}
|
||
.diff-yours .diff-side-header {
|
||
background: #db2828;
|
||
}
|
||
.diff-content {
|
||
margin: 0 !important;
|
||
padding: 8px 0 !important;
|
||
background: #fff !important;
|
||
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
|
||
font-size: 0.85em;
|
||
line-height: 1.5;
|
||
overflow-x: auto;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
.diff-line {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
padding: 0;
|
||
white-space: pre;
|
||
}
|
||
.diff-line-mismatch {
|
||
background: rgba(255, 230, 100, 0.25);
|
||
}
|
||
.diff-expected .diff-line-mismatch {
|
||
background: rgba(33, 186, 69, 0.12);
|
||
}
|
||
.diff-yours .diff-line-mismatch {
|
||
background: rgba(219, 40, 40, 0.10);
|
||
}
|
||
.diff-lineno {
|
||
display: inline-block;
|
||
min-width: 38px;
|
||
padding: 0 8px;
|
||
text-align: right;
|
||
color: #999;
|
||
background: #f5f5f5;
|
||
border-right: 1px solid #e8e8e8;
|
||
user-select: none;
|
||
flex-shrink: 0;
|
||
}
|
||
.diff-text {
|
||
padding: 0 10px;
|
||
flex: 1;
|
||
word-break: break-all;
|
||
white-space: pre-wrap;
|
||
}
|
||
</style>
|
||
|
||
<div class="padding">
|
||
<?php
|
||
// 兼容老变量:$row 是 solution 行(reinfo.php 已查询过)
|
||
$verdict_name = isset($judge_result[$row['result']]) ? $judge_result[$row['result']] : (isset($MSG_UNKNOWN) ? $MSG_UNKNOWN : 'Unknown');
|
||
$verdict_lang = isset($language_name[$lang]) ? $language_name[$lang] : '?';
|
||
$verdict_memory = isset($row['memory']) ? intval($row['memory']) : 0;
|
||
$verdict_time = isset($row['time']) ? intval($row['time']) : 0;
|
||
$verdict_mark = isset($mark) ? $mark : (isset($row['pass_rate']) ? round($row['pass_rate']*100, 1) : 0);
|
||
// 兜底:防止 PHP notice
|
||
$diff_blocks = isset($diff_blocks) && is_array($diff_blocks) ? $diff_blocks : array();
|
||
?>
|
||
|
||
<!-- 1. 顶部判题结果横幅 -->
|
||
<div class="ui segment <?php echo isset($verdict_color_class) ? $verdict_color_class : 'blue'; ?>" id="result-banner">
|
||
<h2 class="ui header">
|
||
<i class="<?php echo isset($result_icon) ? $result_icon : 'info circle' ?> icon" style="color: inherit;"></i>
|
||
<div class="content">
|
||
<?php echo htmlspecialchars($verdict_name, ENT_QUOTES, 'UTF-8') ?>
|
||
<div class="verdict-stats">
|
||
<span class="stat"><i class="chart bar icon"></i><?php echo $MSG_MARK ?>: <?php echo $verdict_mark ?>%</span>
|
||
<span class="stat"><i class="microchip icon"></i><?php echo $verdict_memory ?> KB</span>
|
||
<span class="stat"><i class="clock icon"></i><?php echo $verdict_time ?> ms</span>
|
||
<span class="stat"><i class="code icon"></i><?php echo htmlspecialchars($verdict_lang, ENT_QUOTES, 'UTF-8') ?></span>
|
||
<span class="stat" id="auto-refresh-status" style="display:none;">
|
||
<i class="refresh icon spinning"></i> <span id="auto-refresh-text"></span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</h2>
|
||
</div>
|
||
|
||
<!-- 2. 判题结果标题提示(仅一句话,具体修复建议交给 AI) -->
|
||
<div class="ui info message" id="verdict-tip">
|
||
<i class="info circle icon"></i>
|
||
<?php echo isset($verdict_tip) ? htmlspecialchars($verdict_tip, ENT_QUOTES, 'UTF-8') : ''; ?>
|
||
</div>
|
||
|
||
|
||
<!-- 3. AI 点评区(标题可点击折叠/展开) -->
|
||
<?php if (isset($OJ_LLM_ENABLED) && $OJ_LLM_ENABLED) { ?>
|
||
<div class="ui raised segment" id="llm-section">
|
||
<h3 class="ui header llm-section-header" id="llm-toggle" style="cursor: pointer; user-select: none;">
|
||
<i class="magic icon"></i>
|
||
<div class="content">
|
||
AI 智能点评
|
||
<div class="sub header"><?php echo isset($MSG_AI_REVIEW_DESC) ? $MSG_AI_REVIEW_DESC : '基于你的代码和判题结果分析'; ?></div>
|
||
</div>
|
||
<i class="dropdown icon llm-toggle-icon" style="margin-left: auto; font-size: 1em;"></i>
|
||
</h3>
|
||
<div id="llm-section-body">
|
||
<button class="ui labeled icon primary button" id="llm-review-btn" onclick="fetchLLMReview()">
|
||
<i class="magic icon"></i> 获取 AI 点评
|
||
</button>
|
||
<div id="llm-review-loading" class="ui active inline text loader" style="display:none; margin-left: 1em;">
|
||
AI 正在分析你的代码,请稍候...
|
||
</div>
|
||
<div id="llm-review-result" style="display:none; margin-top: 15px;">
|
||
<div class="ui existing segment" id="llm-review-content"></div>
|
||
</div>
|
||
<div id="llm-review-error" style="display:none; margin-top: 15px;">
|
||
<div class="ui negative message">
|
||
<p id="llm-review-error-msg"></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 折叠/展开 AI 点评区
|
||
(function(){
|
||
var header = document.getElementById('llm-toggle');
|
||
var body = document.getElementById('llm-section-body');
|
||
var icon = header ? header.querySelector('.llm-toggle-icon') : null;
|
||
if (!header || !body) return;
|
||
|
||
header.addEventListener('click', function(e){
|
||
// 如果用户点击的是按钮本身,不要触发展开(按钮事件自己处理)
|
||
if (e.target.closest('button')) return;
|
||
var collapsed = body.style.display === 'none';
|
||
body.style.display = collapsed ? '' : 'none';
|
||
if (icon) {
|
||
icon.className = collapsed
|
||
? 'dropdown icon llm-toggle-icon'
|
||
: 'dropdown icon llm-toggle-icon active';
|
||
// active class 让 Semantic UI 的箭头自动旋转 90°,但我们手写也行
|
||
icon.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(-90deg)';
|
||
}
|
||
});
|
||
})();
|
||
|
||
function fetchLLMReview(){
|
||
var btn = $('#llm-review-btn');
|
||
btn.prop('disabled', true).addClass('loading');
|
||
$('#llm-review-loading').show();
|
||
$('#llm-review-error').hide();
|
||
$('#llm-review-result').hide();
|
||
|
||
var fullText = "";
|
||
var container = document.getElementById('llm-review-content');
|
||
var resultDiv = document.getElementById('llm-review-result');
|
||
var reader = null;
|
||
var decoder = new TextDecoder();
|
||
|
||
var sseBuffer = "";
|
||
function processSSE(text) {
|
||
sseBuffer += text;
|
||
var lines = sseBuffer.split('\n');
|
||
sseBuffer = lines.pop();
|
||
|
||
var currentEvent = "";
|
||
for (var i = 0; i < lines.length; i++) {
|
||
var line = lines[i];
|
||
if (line.indexOf('event: ') === 0) {
|
||
currentEvent = line.substring(7);
|
||
} else if (line.indexOf('data: ') === 0) {
|
||
var dataStr = line.substring(6);
|
||
try {
|
||
var data = JSON.parse(dataStr);
|
||
if (currentEvent === 'chunk') {
|
||
fullText += data.text;
|
||
container.textContent = fullText;
|
||
resultDiv.style.display = 'block';
|
||
} else if (currentEvent === 'cached') {
|
||
fullText = data.text;
|
||
container.textContent = fullText;
|
||
resultDiv.style.display = 'block';
|
||
} else if (currentEvent === 'done') {
|
||
finishReview();
|
||
} else if (currentEvent === 'error') {
|
||
showError(data.message || '未知错误');
|
||
}
|
||
} catch(e) {}
|
||
currentEvent = "";
|
||
}
|
||
}
|
||
}
|
||
|
||
function finishReview() {
|
||
btn.removeClass('loading').hide();
|
||
document.getElementById('llm-review-loading').style.display = 'none';
|
||
resultDiv.style.display = 'block';
|
||
if (fullText && typeof HustOJVditor !== 'undefined' && HustOJVditor.renderMarkdownBlocks) {
|
||
HustOJVditor.renderMarkdownBlocks('#llm-review-content', { useTextContent: true });
|
||
}
|
||
}
|
||
|
||
function showError(msg) {
|
||
btn.removeClass('loading').prop('disabled', false);
|
||
document.getElementById('llm-review-loading').style.display = 'none';
|
||
document.getElementById('llm-review-error-msg').textContent = msg;
|
||
document.getElementById('llm-review-error').style.display = 'block';
|
||
}
|
||
|
||
if (typeof fetch === 'undefined') {
|
||
showError('浏览器不支持,请升级浏览器');
|
||
return;
|
||
}
|
||
|
||
fetch('llm-review.php?sid=<?php echo intval($id); ?>')
|
||
.then(function(response) {
|
||
if (!response.ok) {
|
||
return response.text().then(function(text) {
|
||
try {
|
||
var d = JSON.parse(text);
|
||
showError(d.error || '请求失败 (HTTP ' + response.status + ')');
|
||
} catch(e) {
|
||
showError('请求失败 (HTTP ' + response.status + ')');
|
||
}
|
||
});
|
||
}
|
||
reader = response.body.getReader();
|
||
function read() {
|
||
reader.read().then(function(result) {
|
||
if (result.done) {
|
||
if (fullText) finishReview();
|
||
return;
|
||
}
|
||
var text = decoder.decode(result.value, { stream: true });
|
||
processSSE(text);
|
||
read();
|
||
}).catch(function(err) {
|
||
if (fullText) {
|
||
finishReview();
|
||
} else {
|
||
showError('读取响应失败: ' + err.message);
|
||
}
|
||
});
|
||
}
|
||
read();
|
||
})
|
||
.catch(function(err) {
|
||
showError('网络请求失败: ' + err.message);
|
||
});
|
||
}
|
||
</script>
|
||
<?php } ?>
|
||
|
||
<!-- 3.5. 对比详情(WA/PE 时显示,diff 视图) -->
|
||
<?php if (!empty($diff_blocks) && is_array($diff_blocks)): ?>
|
||
<div id="diff-section">
|
||
<h3 class="ui header" style="margin-top: 0;">
|
||
<i class="exchange icon"></i>
|
||
<div class="content">
|
||
输出对比
|
||
<div class="sub header">逐测试点对比期望输出和你的输出</div>
|
||
</div>
|
||
</h3>
|
||
<?php foreach ($diff_blocks as $idx => $block): ?>
|
||
<div class="ui segment diff-card">
|
||
<div class="diff-header">
|
||
<i class="file outline icon"></i>
|
||
<span class="diff-name"><?php echo htmlspecialchars($block['name'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||
</div>
|
||
<div class="diff-table">
|
||
<div class="diff-side diff-expected">
|
||
<div class="diff-side-header">
|
||
<i class="check icon"></i> 期望输出
|
||
</div>
|
||
<pre class="diff-content"><?php
|
||
$max = max(count($block['expected']), count($block['yours']));
|
||
$lines_html = array();
|
||
for ($li = 0; $li < $max; $li++) {
|
||
$e = isset($block['expected'][$li]) ? $block['expected'][$li] : '';
|
||
$y = isset($block['yours'][$li]) ? $block['yours'][$li] : '';
|
||
$diff_class = ($e !== $y) ? ' diff-line-mismatch' : '';
|
||
$line_no = $li + 1;
|
||
$lines_html[] = '<div class="diff-line' . $diff_class . '" data-line="' . $line_no . '"><span class="diff-lineno">' . $line_no . '</span><span class="diff-text">' . htmlspecialchars($e, ENT_QUOTES, 'UTF-8') . '</span></div>';
|
||
}
|
||
echo implode('', $lines_html);
|
||
?></pre>
|
||
</div>
|
||
<div class="diff-side diff-yours">
|
||
<div class="diff-side-header">
|
||
<i class="bug icon"></i> 你的输出
|
||
</div>
|
||
<pre class="diff-content"><?php
|
||
$max = max(count($block['expected']), count($block['yours']));
|
||
$lines_html = array();
|
||
for ($li = 0; $li < $max; $li++) {
|
||
$e = isset($block['expected'][$li]) ? $block['expected'][$li] : '';
|
||
$y = isset($block['yours'][$li]) ? $block['yours'][$li] : '';
|
||
$diff_class = ($e !== $y) ? ' diff-line-mismatch' : '';
|
||
$line_no = $li + 1;
|
||
$lines_html[] = '<div class="diff-line' . $diff_class . '" data-line="' . $line_no . '"><span class="diff-lineno">' . $line_no . '</span><span class="diff-text">' . htmlspecialchars($y, ENT_QUOTES, 'UTF-8') . '</span></div>';
|
||
}
|
||
echo implode('', $lines_html);
|
||
?></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- 4. 原始错误折叠(默认收起) -->
|
||
<div class="ui styled accordion" id="raw-info-accordion">
|
||
<div class="title">
|
||
<i class="dropdown icon"></i>
|
||
<?php echo isset($MSG_RAW_ERROR) ? $MSG_RAW_ERROR : '查看原始错误信息'; ?>
|
||
<?php if (!empty($view_reinfo_dedup_count)): ?>
|
||
<span class="ui tiny teal label dedup-label">
|
||
<?php echo sprintf(isset($MSG_DEDUP_HINT) ? $MSG_DEDUP_HINT : '已折叠 %d 个相同测试点', intval($view_reinfo_dedup_count)); ?>
|
||
</span>
|
||
<?php endif; ?>
|
||
<?php if (trim($view_reinfo) === ''): ?>
|
||
<span class="dedup-label empty-notice">(无)</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="content">
|
||
<?php if (trim($view_reinfo) === ''): ?>
|
||
<p class="empty-notice">当前判题结果没有原始错误信息(如 TLE/MLE 通常不会写 runtimeinfo)。</p>
|
||
<?php else: ?>
|
||
<pre id="errtxt"><?php
|
||
// 普通用户:折叠后;管理员/源浏览者:原文
|
||
if (isset($is_admin_session) && $is_admin_session) {
|
||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||
} elseif (isset($is_source_browser) && $is_source_browser) {
|
||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||
} else {
|
||
echo htmlspecialchars($view_reinfo, ENT_QUOTES, 'UTF-8');
|
||
}
|
||
?></pre>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 5. 快捷操作区 -->
|
||
<div class="quick-actions">
|
||
<div class="ui horizontal divider">
|
||
<?php echo isset($MSG_QUICK_ACTIONS) ? $MSG_QUICK_ACTIONS : '快捷操作'; ?>
|
||
</div>
|
||
<div class="ui buttons">
|
||
<a class="ui blue button" href="submitpage.php?id=<?php echo intval($row['problem_id']) ?>">
|
||
<i class="edit icon"></i> <?php echo isset($MSG_RESUBMIT) ? $MSG_RESUBMIT : '重新提交'; ?>
|
||
</a>
|
||
<a class="ui button" href="showsource.php?id=<?php echo intval($id) ?>">
|
||
<i class="code icon"></i> <?php echo isset($MSG_VIEW_SOURCE) ? $MSG_VIEW_SOURCE : '查看我的代码'; ?>
|
||
</a>
|
||
<a class="ui button" href="problem.php?id=<?php echo intval($row['problem_id']) ?>">
|
||
<i class="info circle icon"></i> <?php echo isset($MSG_VIEW_PROBLEM) ? $MSG_VIEW_PROBLEM : '查看题目'; ?>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<?php if(isset($OJ_MARKDOWN) && $OJ_MARKDOWN){ ?>
|
||
<script>
|
||
$(document).ready(function(){
|
||
// 初始化折叠面板
|
||
$('#raw-info-accordion').accordion();
|
||
|
||
// 管理员/superuser 默认展开原始错误
|
||
<?php if (isset($is_admin_session) && $is_admin_session) { ?>
|
||
$('#raw-info-accordion').accordion('open', 0);
|
||
<?php } elseif (isset($is_source_browser) && $is_source_browser) { ?>
|
||
$('#raw-info-accordion').accordion('open', 0);
|
||
<?php } ?>
|
||
|
||
HustOJVditor.renderMarkdownBlocks('#errtxt', {
|
||
getSource: function (element) {
|
||
var pre = element.querySelector ? element.querySelector('pre') : null;
|
||
return pre ? pre.textContent : element.textContent || '';
|
||
}
|
||
}).then(function () {
|
||
for(let i=1;i<10;i++){
|
||
$(".language-input"+i).parent().before("<div><?php echo $MSG_Input?>"+i+":</div>");
|
||
$(".language-output"+i).parent().before("<div><?php echo $MSG_Output?>"+i+":</div>");
|
||
}
|
||
|
||
$("#errtxt table").addClass("ui mini-table cell striped");
|
||
$("#errtxt table tr:odd td").css({
|
||
"border": "1px solid grey",
|
||
"text-align": "center",
|
||
"width": "200px",
|
||
"background-color": "#8521d022",
|
||
"height": "30px"
|
||
});
|
||
$("#errtxt table tr:even td").css({
|
||
"border": "1px solid grey",
|
||
"text-align": "center",
|
||
"width": "200px",
|
||
"background-color": "#2185d022",
|
||
"height": "30px"
|
||
});
|
||
$("#errtxt table th").css({
|
||
"border": "1px solid grey",
|
||
"width": "200px",
|
||
"height": "30px",
|
||
"background-color": "#2185d088",
|
||
"text-align": "center"
|
||
});
|
||
<?php
|
||
if(isset($OJ_DOWNLOAD) && $OJ_DOWNLOAD){
|
||
if(isset($OJ_DL_1ST_WA_ONLY) && $OJ_DL_1ST_WA_ONLY){
|
||
?>
|
||
let down=$($("#errtxt").find("h2")[0]);
|
||
let filename=down.text();
|
||
down.html("<a href='download.php?sid=<?php echo $id?>&name=" + filename+ "'>" + filename+ "</a>");
|
||
<?php
|
||
}else{
|
||
?>
|
||
$("#errtxt").find("h2").each(function(){
|
||
let down=$(this);
|
||
let filename=down.text();
|
||
console.log(filename);
|
||
down.html("<a href='download.php?sid=<?php echo $id?>&name=" + filename+ "'>" + filename+ "</a>");
|
||
});
|
||
|
||
<?php
|
||
}
|
||
}
|
||
?>
|
||
$("th").each(function(){
|
||
let html=$(this).html();
|
||
html=html.replace("Expected","<?php echo $MSG_EXPECTED ?>");
|
||
html=html.replace("Yours","<?php echo $MSG_YOURS ?>");
|
||
html=html.replace("filename","<?php echo $MSG_FILENAME ?>");
|
||
html=html.replace("size","<?php echo $MSG_SIZE ?>");
|
||
html=html.replace("result","<?php echo $MSG_RESULT ?>");
|
||
html=html.replace("memory","<?php echo $MSG_MEMORY?>");
|
||
html=html.replace("time","<?php echo $MSG_TIME ?>");
|
||
$(this).html(html);
|
||
|
||
});
|
||
}).catch(function (error) {
|
||
console.error('Failed to render reinfo markdown.', error);
|
||
});
|
||
|
||
});
|
||
</script>
|
||
<?php } else { ?>
|
||
<script>
|
||
// OJ_MARKDOWN 未开启时也要初始化折叠
|
||
$(document).ready(function(){
|
||
$('#raw-info-accordion').accordion();
|
||
<?php if (isset($is_admin_session) && $is_admin_session) { ?>
|
||
$('#raw-info-accordion').accordion('open', 0);
|
||
<?php } elseif (isset($is_source_browser) && $is_source_browser) { ?>
|
||
$('#raw-info-accordion').accordion('open', 0);
|
||
<?php } ?>
|
||
});
|
||
</script>
|
||
<?php } ?>
|
||
|
||
<!-- 自动刷新:判题中(result < 4 或 > 14)时定时拉取最新状态 -->
|
||
<?php
|
||
$result_code_js = isset($row['result']) ? intval($row['result']) : 4;
|
||
$sid_js = isset($id) ? intval($id) : 0;
|
||
// 0=Pending, 1=Pending Rejudging, 2=Compiling, 3=Running/Judging, 14=Manual, 15=Submitting, 16/17=Remote
|
||
$is_pending = ($result_code_js < 4) || ($result_code_js > 14);
|
||
?>
|
||
<?php if ($sid_js > 0 && $is_pending): ?>
|
||
<script>
|
||
(function(){
|
||
var sid = <?php echo $sid_js ?>;
|
||
var currentResult = <?php echo $result_code_js ?>;
|
||
var interval = 1500; // 初始 1.5s
|
||
var maxInterval = 8000; // 最长 8s
|
||
var countdown = 0;
|
||
var stopped = false;
|
||
|
||
function showStatus(text){
|
||
var el = document.getElementById('auto-refresh-status');
|
||
var t = document.getElementById('auto-refresh-text');
|
||
if (el && t) {
|
||
el.style.display = '';
|
||
t.textContent = text;
|
||
}
|
||
}
|
||
|
||
function hideStatus(){
|
||
var el = document.getElementById('auto-refresh-status');
|
||
if (el) el.style.display = 'none';
|
||
}
|
||
|
||
function tick(){
|
||
if (stopped) return;
|
||
countdown--;
|
||
if (countdown > 0) {
|
||
showStatus('将在 ' + countdown + ' 秒后自动刷新');
|
||
setTimeout(tick, 1000);
|
||
return;
|
||
}
|
||
fetch('status-ajax.php?solution_id=' + sid + '&t=json', {credentials: 'same-origin'})
|
||
.then(function(r){ return r.json(); })
|
||
.then(function(data){
|
||
if (!data || typeof data.result === 'undefined') {
|
||
// 解析失败,下次重试
|
||
interval = Math.min(interval * 1.5, maxInterval);
|
||
countdown = Math.ceil(interval / 1000);
|
||
tick();
|
||
return;
|
||
}
|
||
var newResult = parseInt(data.result);
|
||
if (newResult >= 4 && newResult <= 14) {
|
||
// 判题完成,刷新页面拿到完整错误信息
|
||
showStatus('判题完成,正在刷新...');
|
||
setTimeout(function(){ location.reload(); }, 400);
|
||
stopped = true;
|
||
return;
|
||
}
|
||
// 仍在评判中
|
||
interval = Math.min(interval * 1.3, maxInterval);
|
||
countdown = Math.ceil(interval / 1000);
|
||
showStatus('判题中,将在 ' + countdown + ' 秒后再次查询');
|
||
setTimeout(tick, 1000);
|
||
})
|
||
.catch(function(err){
|
||
// 网络错误,重试
|
||
console.warn('auto refresh failed:', err);
|
||
interval = Math.min(interval * 1.5, maxInterval);
|
||
countdown = Math.ceil(interval / 1000);
|
||
showStatus('查询失败,' + countdown + ' 秒后重试');
|
||
setTimeout(tick, 1000);
|
||
});
|
||
}
|
||
|
||
// 用户切到后台标签时停止轮询,避免无意义请求
|
||
document.addEventListener('visibilitychange', function(){
|
||
if (document.hidden) {
|
||
stopped = true;
|
||
hideStatus();
|
||
} else if (!stopped) {
|
||
countdown = 0;
|
||
tick();
|
||
}
|
||
});
|
||
|
||
// 启动
|
||
countdown = Math.ceil(interval / 1000);
|
||
showStatus('判题中,将在 ' + countdown + ' 秒后自动刷新');
|
||
setTimeout(tick, 1000);
|
||
})();
|
||
</script>
|
||
<?php endif; ?>
|
||
|
||
<?php include("template/$OJ_TEMPLATE/footer.php");?>
|