Browse Source

UI improvement on submission / submissions page

pull/6/head
Menci 6 years ago
parent
commit
c90077da58
  1. 7
      modules/submission.js
  2. 19
      static/style.css
  3. 2
      views/footer.ejs
  4. 2
      views/header.ejs
  5. 8
      views/status_label.ejs
  6. 36
      views/submission.ejs
  7. 25
      views/submissions.ejs
  8. 10
      views/submissions_item.ejs

7
modules/submission.js

@ -71,9 +71,9 @@ app.get('/submissions', async (req, res) => {
if (req.query.problem_id) { if (req.query.problem_id) {
let problem_id = parseInt(req.query.problem_id); let problem_id = parseInt(req.query.problem_id);
let problem = await Problem.fromID(problem_id); let problem = await Problem.fromID(problem_id);
if(!problem) if (!problem)
throw new ErrorMessage("无此题目。"); throw new ErrorMessage("无此题目。");
if(await problem.isAllowedUseBy(res.locals.user)) { if (await problem.isAllowedUseBy(res.locals.user)) {
where.problem_id = { where.problem_id = {
$and: [ $and: [
{ $eq: where.problem_id = problem_id } { $eq: where.problem_id = problem_id }
@ -91,6 +91,8 @@ app.get('/submissions', async (req, res) => {
if (req.query.problem_id) where.problem_id = parseInt(req.query.problem_id) || -1; if (req.query.problem_id) where.problem_id = parseInt(req.query.problem_id) || -1;
} }
let isFiltered = !!(where.problem_id || where.user_id || where.score || where.language || where.status);
let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state); let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state);
let judge_state = await JudgeState.query(paginate, where, [['id', 'desc']], true); let judge_state = await JudgeState.query(paginate, where, [['id', 'desc']], true);
@ -112,6 +114,7 @@ app.get('/submissions', async (req, res) => {
pushType: 'rough', pushType: 'rough',
form: req.query, form: req.query,
displayConfig: displayConfig, displayConfig: displayConfig,
isFiltered: isFiltered
}); });
} catch (e) { } catch (e) {
syzoj.log(e); syzoj.log(e);

19
static/style.css

@ -21,15 +21,15 @@ h4,
h5, h5,
body body
{ {
font-family: font-family:
Lato, Lato,
-apple-system, -apple-system,
'PingFang SC',/* Apple */ 'PingFang SC',/* Apple */
'Source Han Sans SC', 'Source Han Sans SC',
'Noto Sans CJK SC', /* Google */ 'Noto Sans CJK SC', /* Google */
'Microsoft Yahei', 'Microsoft Yahei',
'Lantinghei SC', 'Lantinghei SC',
'Hiragino Sans GB', 'Hiragino Sans GB',
'Microsoft Sans Serif', /* M$ */ 'Microsoft Sans Serif', /* M$ */
'WenQuanYi Micro Hei', /* *nix */ 'WenQuanYi Micro Hei', /* *nix */
sans-serif; sans-serif;
@ -300,3 +300,8 @@ code {
font-size: 1em; font-size: 1em;
vertical-align: initial; vertical-align: initial;
} }
.ui.selection.dropdown .menu>.item {
padding-left: 0.8rem !important;
padding-right: 0 !important;
}

2
views/footer.ejs

@ -1,5 +1,5 @@
</div> </div>
<div class="ui vertical footer segment" style="margin-top: 10px; "> <div class="ui vertical footer segment" style="margin-top: 15px; ">
<div class="ui center aligned container"> <div class="ui center aligned container">
<span style="color: #999;"><%= syzoj.config.title %> Powered by <a href="https://github.com/syzoj/syzoj" target="_blank">SYZOJ</a>.</span> <span style="color: #999;"><%= syzoj.config.title %> Powered by <a href="https://github.com/syzoj/syzoj" target="_blank">SYZOJ</a>.</span>
</div> </div>

2
views/header.ejs

@ -11,7 +11,7 @@
<link href="/mathjax.css?20181105" rel="stylesheet"> <link href="/mathjax.css?20181105" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/KaTeX/0.10.0/katex.min.css" rel="stylesheet"> <link href="https://cdnjs.loli.net/ajax/libs/KaTeX/0.10.0/katex.min.css" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/morris.js/0.5.1/morris.css" rel="stylesheet"> <link href="https://cdnjs.loli.net/ajax/libs/morris.js/0.5.1/morris.css" rel="stylesheet">
<link href="/style.css?20181108" rel="stylesheet"> <link href="/style.css?2018110801" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Fira+Mono" rel="stylesheet"> <link href="https://fonts.loli.net/css?family=Fira+Mono" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Lato:400,700,400italic,700italic&subset=latin" rel="stylesheet"> <link href="https://fonts.loli.net/css?family=Lato:400,700,400italic,700italic&subset=latin" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&amp;subset=latin-ext" rel="stylesheet"> <link href="https://fonts.loli.net/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&amp;subset=latin-ext" rel="stylesheet">

8
views/status_label.ejs

@ -20,7 +20,7 @@ const iconList = {
}; };
Vue.component('status-label', { Vue.component('status-label', {
template: '#statusIconTemplate', template: '#statusIconTemplate',
props: ['status', 'indetail'], props: ['status', 'indetail', 'progress'],
computed: { computed: {
icon() { icon() {
if (this.status in iconList) { if (this.status in iconList) {
@ -31,6 +31,10 @@ Vue.component('status-label', {
}, },
colorClass() { colorClass() {
return (this.indetail ? 'status_detail ' : '') + this.status.toLowerCase().split(' ').join('_'); return (this.indetail ? 'status_detail ' : '') + this.status.toLowerCase().split(' ').join('_');
},
outputStatus() {
if (this.status === 'Running' && this.progress) return 'Running ' + this.progress.finished + '/' + this.progress.total;
else return this.status;
} }
} }
}) })
@ -38,6 +42,6 @@ Vue.component('status-label', {
<script type="text/x-template" id="statusIconTemplate"> <script type="text/x-template" id="statusIconTemplate">
<span class="status" :class="colorClass"> <span class="status" :class="colorClass">
<i class="icon" :class="icon"></i> <i class="icon" :class="icon"></i>
{{ status }} {{ outputStatus }}
</span> </span>
</script> </script>

36
views/submission.ejs

@ -47,7 +47,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr is="submission-item" v-bind:data="roughData" :config="displayConfig" :show-rejudge="showRejudge"></tr> <tr is="submission-item" v-bind:data="roughData" :config="displayConfig" :show-rejudge="showRejudge" :progress="getProgress()"></tr>
</tbody> </tbody>
</table> </table>
@ -64,7 +64,7 @@
子任务 #{{ $index + 1 }} 子任务 #{{ $index + 1 }}
</div> </div>
<div class="four wide column"> <div class="four wide column">
<status-label :status="getSubtaskResult(subtask)" :indetail="true"></status-label> <status-label :status="getSubtaskResult(subtask)" :indetail="true" :progress="getProgress($index)"></status-label>
</div> </div>
<div class="three wide column" v-if="subtask.score != null"> <div class="three wide column" v-if="subtask.score != null">
得分:<span style="font-weight: normal; ">{{ Math.trunc(subtask.score) }}</span> 得分:<span style="font-weight: normal; ">{{ Math.trunc(subtask.score) }}</span>
@ -74,7 +74,7 @@
<div class="content" :class="singleSubtask ? 'active' : ''"> <div class="content" :class="singleSubtask ? 'active' : ''">
<div class="accordion"> <div class="accordion">
<template v-for="curCase, $caseIndex in subtask.cases"> <template v-for="curCase, $caseIndex in subtask.cases">
<div class="title"> <div class="title" :class="checkTestcaseOK(curCase) ? '' : 'unexpandable'">
<div class="ui grid"> <div class="ui grid">
<div class="three wide column"> <div class="three wide column">
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
@ -234,10 +234,38 @@ const vueApp = new Vue({
}, },
checkTestcaseOK(c) { checkTestcaseOK(c) {
return c.status === TaskStatus.Done; return c.status === TaskStatus.Done;
},
getProgress(i) {
if (!this.detailResult || !this.detailResult.judge || !this.detailResult.judge.subtasks) return {
finished: 0,
total: 0
};
let isPending = status => [TaskStatus.Waiting, TaskStatus.Running].includes(status);
let subtaskProgress = [], allFinished = 0, allTotal = 0;
for (let i in this.detailResult.judge.subtasks) {
let subtaskFinished = 0, subtaskTotal = 0;
for (let j in this.detailResult.judge.subtasks[i].cases) {
subtaskTotal++, allTotal++;
if (!isPending(this.detailResult.judge.subtasks[i].cases[j].status)) subtaskFinished++, allFinished++;
}
subtaskProgress.push({
finished: subtaskFinished,
total: subtaskTotal
});
}
let allProgress = {
finished: allFinished,
total: allTotal
};
return typeof i === 'undefined' ? allProgress : subtaskProgress[i];
} }
}, },
mounted() { mounted() {
$(document).ready(function(){ $('.ui.accordion').accordion()}); $(document).ready(function(){ $('.ui.accordion').accordion({ selector: { trigger: '.title:not(.unexpandable)' } })});
}, },
updated() { updated() {
$('.ui.accordion').accordion("refresh"); $('.ui.accordion').accordion("refresh");

25
views/submissions.ejs

@ -36,10 +36,10 @@
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="default text"></div> <div class="default text"></div>
<div class="menu"> <div class="menu">
<div class="item" data-value="">不限</div> <div class="item" data-value=""><b>不限</b></div>
<div class="item" data-value="submit-answer">提交答案</div> <div class="item" data-value="submit-answer"><b>提交答案</b></div>
<% for (let lang in syzoj.config.languages) { %> <% for (let lang in syzoj.config.languages) { %>
<div class="item" data-value="<%= lang %>"><%= syzoj.config.languages[lang].show %></div> <div class="item" data-value="<%= lang %>"><b><%= syzoj.config.languages[lang].show %></b></div>
<% } %> <% } %>
</div> </div>
</div> </div>
@ -52,10 +52,10 @@
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="default text"></div> <div class="default text"></div>
<div class="menu"> <div class="menu">
<div class="item" data-value="">不限<i class="dropdown icon" style="visibility: hidden; "></i></div> <div class="item" data-value=""><b>不限</b><i class="dropdown icon" style="visibility: hidden; "></i></div>
<% for (let status in this.icon) { %> <% for (let status in this.icon) { %>
<% if (this.iconHidden.includes(status)) continue; %> <% if (this.iconHidden.includes(status)) continue; %>
<div class="item" data-value="<%= status %>"><span class="status <%= status.toLowerCase().split(' ').join('_') %>"><i class="<%= this.icon[status] %> icon"></i> <%= status %></div> <div class="item" data-value="<%= status %>"><span class="status <%= status.toLowerCase().split(' ').join('_') %>"><i class="<%= this.icon[status] %> icon"></i> <b><%= status %></b></div>
<% } %> <% } %>
</div> </div>
</div> </div>
@ -100,8 +100,23 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<% if (!items.length) { %>
<div style="background-color: #fff; height: 18px; margin-top: -18px; "></div>
<div class="ui placeholder segment" style="margin-top: -5px; ">
<div class="ui icon header">
<% if (isFiltered) { %>
<i class="ui search icon" style="margin-bottom: 20px; "></i>
找不到符合条件的提交
<% } else { %>
<i class="ui file icon" style="margin-bottom: 20px; "></i>
暂无提交
<% } %>
</div>
</div>
<% } else { %>
<br> <br>
<% include page %> <% include page %>
<% } %>
</div> </div>
<script src="https://cdnjs.loli.net/ajax/libs/vue/2.4.2/vue.min.js"></script> <script src="https://cdnjs.loli.net/ajax/libs/vue/2.4.2/vue.min.js"></script>

10
views/submissions_item.ejs

@ -4,17 +4,17 @@
<script src="https://cdnjs.loli.net/ajax/libs/textfit/2.3.1/textFit.min.js"></script> <script src="https://cdnjs.loli.net/ajax/libs/textfit/2.3.1/textFit.min.js"></script>
<script> <script>
const submissionUrl = <%- JSON.stringify(displayConfig.inContest ? const submissionUrl = <%- JSON.stringify(displayConfig.inContest ?
syzoj.utils.makeUrl(['contest', 'submission', 'VanDarkholme']) : syzoj.utils.makeUrl(['contest', 'submission', 'VanDarkholme']) :
syzoj.utils.makeUrl(['submission', 'VanDarkholme'])) %>; syzoj.utils.makeUrl(['submission', 'VanDarkholme'])) %>;
const problemUrl = <%- JSON.stringify(displayConfig.inContest ? const problemUrl = <%- JSON.stringify(displayConfig.inContest ?
syzoj.utils.makeUrl(['contest', contest.id, 'problem', 'VanDarkholme']) : syzoj.utils.makeUrl(['contest', contest.id, 'problem', 'VanDarkholme']) :
syzoj.utils.makeUrl(['problem', 'VanDarkholme'])) %>; syzoj.utils.makeUrl(['problem', 'VanDarkholme'])) %>;
const userUrl = <%- JSON.stringify(syzoj.utils.makeUrl(['user', 'VanDarkholme'])) %>; const userUrl = <%- JSON.stringify(syzoj.utils.makeUrl(['user', 'VanDarkholme'])) %>;
Vue.component('submission-item', { Vue.component('submission-item', {
template: '#submissionItemTemplate', template: '#submissionItemTemplate',
props: ['data', 'config', 'showRejudge'], props: ['data', 'config', 'showRejudge', 'progress'],
computed: { computed: {
statusString() { statusString() {
const data = this.data; const data = this.data;
@ -57,9 +57,9 @@ Vue.component('submission-item', {
<% } %> <% } %>
<td ref="problemLabel"><a ref="problemLabelTextFit" style="width: 230px; height: 22px; display: block; margin: 0 auto; line-height: 22px;" :href="problemLink"><b>#{{ config.inContest ? alpha(data.info.problemId) : data.info.problemId }}.</b> {{ data.info.problemName }}</a></td> <td ref="problemLabel"><a ref="problemLabelTextFit" style="width: 230px; height: 22px; display: block; margin: 0 auto; line-height: 22px;" :href="problemLink"><b>#{{ config.inContest ? alpha(data.info.problemId) : data.info.problemId }}.</b> {{ data.info.problemName }}</a></td>
<% if (active === 'submissions') { %> <% if (active === 'submissions') { %>
<td><a :href="submissionLink"><b><status-label :status="statusString"></status-label></b></a></td> <td><a :href="submissionLink"><b><status-label :status="statusString" :progress="progress"></status-label></b></a></td>
<% } else { %> <% } else { %>
<td><b><status-label :status="statusString"></status-label></b></td> <td><b><status-label :status="statusString" :progress="progress"></status-label></b></td>
<% } %> <% } %>
<template v-if="data.result"> <template v-if="data.result">

Loading…
Cancel
Save