Skip to content

Commit c8e1316

Browse files
ZnPdCoclaude
andcommitted
refactor(exportutil): clean up code style and template DOM
- Remove redundant "what" comments from buildExportJson - Simplify updateHeaderIndicators by reducing nesting - Unify DOM construction in template (innerHTML -> DOM API) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 70713da commit c8e1316

3 files changed

Lines changed: 22 additions & 40 deletions

File tree

src/component/exportutil/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
本模块负责将比赛成绩导出为 HTML 或 CSV 格式。
44

5-
HTML 导出采用 **JSON + 模板** 方案:C++ 端通过 `buildExportJson()` 构建包含全部比赛数据的 JSON 对象,然后将其嵌入 `export_template.html` 中的 `%%DATA%%` 占位符,由浏览器端 JavaScript 完成页面渲染。这种方式将数据构造与页面展示解耦,C++ 代码只需关注 JSON 结构,无需拼接 HTML 字符串。
5+
HTML 导出采用 JSON + 模板 方案:C++ 端通过 `buildExportJson()` 构建包含全部比赛数据的 JSON 对象,然后将其嵌入 `export_template.html` 中的 `%%DATA%%` 占位符,由浏览器端 JavaScript 完成页面渲染。这种方式将数据构造与页面展示解耦,C++ 代码只需关注 JSON 结构,无需拼接 HTML 字符串。
66

77
以下是 JSON 数据的结构说明。
88

@@ -72,7 +72,7 @@ HTML 导出采用 **JSON + 模板** 方案:C++ 端通过 `buildExportJson()`
7272
| `input` | `string` | 该测试点输入文件名 | `"plus2.in"` |
7373
| `result` | `string` | 评测状态结果文字 | `"评测通过"` |
7474
| `time` | `string` | 运行耗时字符串 | `"0.005 s"` |
75-
| `memory` | `string` | 内存占用字符串 | `"5.34 MB"` |
75+
| `memory` | `string` | 内存占用字符串 | `"5.34 MiB"` |
7676
| `score` | `number` | 该项得分 | `20` |
7777
| `full_score` | `number` | 该项满分 | `20` |
7878
| `bg` | `string` | 状态背景 (RGB) | `"rgb(192, 255, 192)"` |
@@ -119,18 +119,18 @@ HTML 导出采用 **JSON + 模板** 方案:C++ 端通过 `buildExportJson()`
119119
"bg": "120, 30%, 70%",
120120
"file": "plus.cpp",
121121
"details": [
122-
{ "label": "#1", "row_span": 1, "input": "plus1.in", "result": "评测通过", "time": "0.001 s", "memory": "1.2 MB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" },
123-
{ "label": "#2", "row_span": 1, "input": "plus2.in", "result": "评测通过", "time": "0.002 s", "memory": "1.2 MB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" }
122+
{ "label": "#1", "row_span": 1, "input": "plus1.in", "result": "评测通过", "time": "0.001 s", "memory": "1.2 MiB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" },
123+
{ "label": "#2", "row_span": 1, "input": "plus2.in", "result": "评测通过", "time": "0.002 s", "memory": "1.2 MiB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" }
124124
]
125125
},
126126
{
127127
"score": 50,
128128
"bg": "120,28.9006%,82.3499%",
129129
"file": "minus.cpp",
130130
"details": [
131-
{ "label": "#1", "row_span": 1, "input": "minus1.in", "result": "评测通过", "time": "0.001 s", "memory": "1.2 MB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" },
132-
{ "label": "#2<br>子任务依赖情况:Pure", "row_span": 2, "input": "minus2.in", "result": "答案错误", "time": "0.001 s", "memory": "2.0 MB", "score": 0, "full_score": 50, "bg": "rgb(255, 192, 192)", "info": "在第四行,读取到 123456,但期望 789123" },
133-
{ "label": "", "row_span": 0, "input": "minus3.in", "result": "运行错误", "time": "0.001 s", "memory": "2.0 MB", "score": 0, "full_score": 50, "bg": "rgb(255, 192, 192)" }
131+
{ "label": "#1", "row_span": 1, "input": "minus1.in", "result": "评测通过", "time": "0.001 s", "memory": "1.2 MiB", "score": 50, "full_score": 50, "bg": "rgb(192, 255, 192)" },
132+
{ "label": "#2<br>子任务依赖情况:Pure", "row_span": 2, "input": "minus2.in", "result": "答案错误", "time": "0.001 s", "memory": "2.0 MiB", "score": 0, "full_score": 50, "bg": "rgb(255, 192, 192)", "info": "在第四行,读取到 123456,但期望 789123" },
133+
{ "label": "", "row_span": 0, "input": "minus3.in", "result": "运行错误", "time": "0.001 s", "memory": "2.0 MiB", "score": 0, "full_score": 50, "bg": "rgb(255, 192, 192)" }
134134
]
135135
}
136136
]

src/component/exportutil/export_template.html

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,29 +110,17 @@
110110

111111
function updateHeaderIndicators() {
112112
const ths = thead.querySelectorAll('th');
113-
ths.forEach((th, idx) => {
113+
ths.forEach((th) => {
114114
const indicator = th.querySelector('.sort-indicator');
115115
if (!indicator) return;
116116

117-
if (idx >= 2) {
118-
const col = th.dataset.col;
119-
if (col === 'total' && sortState.key === 'total') {
120-
indicator.textContent = sortState.asc ? '▴' : '▾';
121-
indicator.style.visibility = 'visible';
122-
} else if (col === 'task') {
123-
const ti = parseInt(th.dataset.taskIndex, 10);
124-
if (sortState.key === 'task' && sortState.taskIndex === ti) {
125-
indicator.textContent = sortState.asc ? '▴' : '▾';
126-
indicator.style.visibility = 'visible';
127-
} else {
128-
indicator.style.visibility = 'hidden';
129-
}
130-
} else {
131-
indicator.style.visibility = 'hidden';
132-
}
133-
} else {
134-
indicator.style.visibility = 'hidden';
135-
}
117+
const isActive =
118+
(sortState.key === 'total' && th.dataset.col === 'total') ||
119+
(sortState.key === 'task' && th.dataset.col === 'task' &&
120+
parseInt(th.dataset.taskIndex, 10) === sortState.taskIndex);
121+
122+
indicator.textContent = sortState.asc ? '▴' : '▾';
123+
indicator.style.visibility = isActive ? 'visible' : 'hidden';
136124
});
137125
}
138126

@@ -275,7 +263,12 @@
275263
const scoreTd = document.createElement('td');
276264
scoreTd.rowSpan = d.row_span;
277265
scoreTd.style.background = d.bg;
278-
scoreTd.innerHTML = `<span style="font-weight: bold; font-size: large;">${d.score}</span> / ${d.full_score}`;
266+
const scoreSpan = document.createElement('span');
267+
scoreSpan.style.fontWeight = 'bold';
268+
scoreSpan.style.fontSize = 'large';
269+
scoreSpan.textContent = d.score;
270+
scoreTd.appendChild(scoreSpan);
271+
scoreTd.appendChild(document.createTextNode(` / ${d.full_score}`));
279272
dTr.append(scoreTd);
280273
}
281274
dTbody.append(dTr);

src/component/exportutil/exportutil.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
4141
QList<Task *> taskList = contest->getTaskList();
4242
int sfullScore = contest->getTotalScore();
4343

44-
// Sort and rank
4544
QList<std::pair<int, QString>> sortList;
4645

4746
for (auto &i : contestantList) {
@@ -65,12 +64,10 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
6564
}
6665
}
6766

68-
// Top-level fields
6967
QJsonObject root;
7068
root["name"] = contest->getContestTitle();
7169
root["version"] = QString("Lemonlime Version %1:%2").arg(LEMON_VERSION_STRING).arg(LEMON_VERSION_BUILD);
7270

73-
// i18n
7471
QJsonObject i18n;
7572
i18n["rank_list"] = tr("Rank List");
7673
i18n["hint"] = tr("Click names or task scores to jump to details. Judged By LemonLime");
@@ -90,7 +87,6 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
9087
i18n["return_to_top"] = tr("Return to top");
9188
root["i18n"] = i18n;
9289

93-
// task_names
9490
QJsonArray taskNames;
9591

9692
for (auto &task : taskList) {
@@ -99,7 +95,6 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
9995

10096
root["task_names"] = taskNames;
10197

102-
// contestants
10398
QJsonArray contestantsArr;
10499

105100
for (int idx = 0; idx < contestantList.size(); idx++) {
@@ -159,7 +154,6 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
159154
tObj["bg"] = QString("0, 0%, 90%");
160155
}
161156

162-
// source file
163157
if (taskList[j]->getTaskType() == Task::Traditional ||
164158
taskList[j]->getTaskType() == Task::Interaction ||
165159
taskList[j]->getTaskType() == Task::Communication ||
@@ -169,7 +163,6 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
169163
}
170164
}
171165

172-
// details
173166
bool isAnswersOnly = taskList[j]->getTaskType() == Task::AnswersOnly;
174167
bool canShowDetails = contestant->getCheckJudged(j) &&
175168
(isAnswersOnly || contestant->getCompileState(j) == CompileSuccessfully);
@@ -223,7 +216,7 @@ QJsonObject ExportUtil::buildExportJson(Contest *contest) {
223216

224217
if (memoryUsed[jj][k] != -1) {
225218
dObj["memory"] =
226-
QString("").asprintf("%.3lf MB", double(memoryUsed[jj][k]) / 1024 / 1024);
219+
QString("").asprintf("%.3lf MiB", double(memoryUsed[jj][k]) / 1024 / 1024);
227220
} else {
228221
dObj["memory"] = tr("Invalid");
229222
}
@@ -276,7 +269,6 @@ void ExportUtil::exportHtml(QWidget *widget, Contest *contest, const QString &fi
276269

277270
QApplication::setOverrideCursor(Qt::WaitCursor);
278271

279-
// Read template from Qt resource
280272
QFile templateFile(":/export/export_template.html");
281273

282274
if (! templateFile.open(QFile::ReadOnly | QFile::Text)) {
@@ -288,15 +280,12 @@ void ExportUtil::exportHtml(QWidget *widget, Contest *contest, const QString &fi
288280
QString htmlTemplate = templateFile.readAll();
289281
templateFile.close();
290282

291-
// Build JSON data
292283
QJsonObject jsonData = buildExportJson(contest);
293284
QJsonDocument doc(jsonData);
294285
QString jsonStr = doc.toJson(QJsonDocument::Compact);
295286

296-
// Replace placeholder with actual data
297287
htmlTemplate.replace("%%DATA%%", jsonStr);
298288

299-
// Write output
300289
QTextStream out(&file);
301290
out << htmlTemplate;
302291
out.flush();

0 commit comments

Comments
 (0)