Browse Source

Add admin pages

pull/6/head
Menci 7 years ago
parent
commit
73e46e6bf6
  1. 1
      app.js
  2. 347
      modules/admin.js
  3. 17
      static/script.js
  4. 15
      utility.js
  5. 25
      views/admin.ejs
  6. 54
      views/admin_config.ejs
  7. 3
      views/admin_footer.ejs
  8. 24
      views/admin_header.ejs
  9. 79
      views/admin_info.ejs
  10. 78
      views/admin_links.ejs
  11. 63
      views/admin_privilege.ejs
  12. 34
      views/admin_raw.ejs
  13. 108
      views/admin_rejudge.ejs
  14. 3
      views/header.ejs

1
app.js

@ -23,6 +23,7 @@ let fs = require('fs'),
path = require('path');
global.syzoj = {
rootDir: __dirname,
config: require('./config.json'),
models: [],
modules: [],

347
modules/admin.js

@ -0,0 +1,347 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
let Problem = syzoj.model('problem');
let JudgeState = syzoj.model('judge_state');
let Article = syzoj.model('article');
let Contest = syzoj.model('contest');
let User = syzoj.model('user');
let UserPrivilege = syzoj.model('user_privilege');
let db = syzoj.db;
app.get('/admin/info', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
let allSubmissionsCount = await JudgeState.count();
let todaySubmissionsCount = await JudgeState.count({ submit_time: { $gte: syzoj.utils.getCurrentDate(true) } });
let problemsCount = await Problem.count();
let articlesCount = await Article.count();
let contestsCount = await Contest.count();
let usersCount = await User.count();
res.render('admin_info', {
allSubmissionsCount: allSubmissionsCount,
todaySubmissionsCount: todaySubmissionsCount,
problemsCount: problemsCount,
articlesCount: articlesCount,
contestsCount: contestsCount,
usersCount: usersCount
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
let configItems = {
'title': { name: '站点标题', type: String },
'邮箱验证': null,
'register_mail.enabled': { name: '启用', type: Boolean },
'register_mail.address': { name: '发件人地址', type: String },
'register_mail.key': { name: '密钥', type: String },
'默认参数': null,
'default.problem.time_limit': { name: '时间限制(单位:ms)', type: Number },
'default.problem.memory_limit': { name: '空间限制(单位:MiB)', type: Number },
'限制': null,
'limit.time_limit': { name: '最大时间限制(单位:ms)', type: Number },
'limit.memory_limit': { name: '最大空间限制(单位:MiB)', type: Number },
'limit.data_size': { name: '所有数据包大小(单位:byte)', type: Number },
'limit.testdata': { name: '测试数据大小(单位:byte)', type: Number },
'limit.submit_code': { name: '代码长度(单位:byte)', type: Number },
'limit.submit_answer': { name: '提交答案题目答案大小(单位:byte)', type: Number },
'limit.custom_test_input': { name: '自定义测试输入文件大小(单位:byte)', type: Number },
'limit.testdata_filecount': { name: '测试数据文件数量(单位:byte)', type: Number },
'每页显示数量': null,
'page.problem': { name: '题库', type: Number },
'page.judge_state': { name: '提交记录', type: Number },
'page.problem_statistics': { name: '题目统计', type: Number },
'page.ranklist': { name: '排行榜', type: Number },
'page.discussion': { name: '讨论', type: Number },
'page.article_comment': { name: '评论', type: Number },
'page.contest': { name: '比赛', type: Number },
'编译器版本': null,
'languages.cpp.version': { name: 'C++', type: String },
'languages.cpp11.version': { name: 'C++11', type: String },
'languages.csharp.version': { name: 'C#', type: String },
'languages.c.version': { name: 'C', type: String },
'languages.vala.version': { name: 'Vala', type: String },
'languages.java.version': { name: 'Java', type: String },
'languages.pascal.version': { name: 'Pascal', type: String },
'languages.lua.version': { name: 'Lua', type: String },
'languages.luajit.version': { name: 'LuaJIT', type: String },
'languages.python2.version': { name: 'Python 2', type: String },
'languages.python3.version': { name: 'Python 3', type: String },
'languages.nodejs.version': { name: 'Node.js', type: String },
'languages.ruby.version': { name: 'Ruby', type: String },
'languages.haskell.version': { name: 'Haskell', type: String },
'languages.ocaml.version': { name: 'OCaml', type: String },
'languages.vbnet.version': { name: 'Visual Basic', type: String }
};
app.get('/admin/config', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
for (let i in configItems) {
if (!configItems[i]) continue;
configItems[i].val = eval(`syzoj.config.${i}`);
}
res.render('admin_config', {
items: configItems
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.post('/admin/config', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
for (let i in configItems) {
if (!configItems[i]) continue;
if (req.body[i]) {
let val;
if (configItems[i].type === Boolean) {
val = req.body[i] === 'on';
} else {
val = req.body[i];
}
let f = new Function('val', `syzoj.config.${i} = val`);
f(val);
}
}
await syzoj.utils.saveConfig();
res.redirect(syzoj.utils.makeUrl(['admin', 'config']));
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.get('/admin/privilege', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
let a = await UserPrivilege.query();
let users = {};
for (let p of a) {
if (!users[p.user_id]) {
users[p.user_id] = {
user: await User.fromID(p.user_id),
privileges: []
};
}
users[p.user_id].privileges.push(p.privilege);
}
res.render('admin_privilege', {
users: Object.values(users)
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.post('/admin/privilege', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
let data = JSON.parse(req.body.data);
for (let id in data) {
let user = await User.fromID(id);
if (!user) throw new ErrorMessage(`不存在 ID 为 ${id} 的用户。`);
await user.setPrivileges(data[id]);
}
res.redirect(syzoj.utils.makeUrl(['admin', 'privilege']));
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.get('/admin/rejudge', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
res.render('admin_rejudge', {
form: {},
count: null
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.post('/admin/rejudge', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
let user = await User.fromName(req.body.submitter || '');
let where = {};
if (user) where.user_id = user.id;
else if (req.body.submitter) where.user_id = -1;
let minID = parseInt(req.body.min_id);
if (isNaN(minID)) minID = 0;
let maxID = parseInt(req.body.max_id);
if (isNaN(maxID)) maxID = 2147483647;
where.id = {
$and: {
$gte: parseInt(minID),
$lte: parseInt(maxID)
}
};
let minScore = parseInt(req.body.min_score);
if (isNaN(minScore)) minScore = 0;
let maxScore = parseInt(req.body.max_score);
if (isNaN(maxScore)) maxScore = 100;
where.score = {
$and: {
$gte: parseInt(minScore),
$lte: parseInt(maxScore)
}
};
let minTime = syzoj.utils.parseDate(req.body.min_time);
if (isNaN(minTime)) minTime = 0;
let maxTime = syzoj.utils.parseDate(req.body.max_time);
if (isNaN(maxTime)) maxTime = 2147483647;
where.submit_time = {
$and: {
$gte: parseInt(minTime),
$lte: parseInt(maxTime)
}
};
if (req.body.language) {
if (req.body.language === 'submit-answer') where.language = '';
else where.language = req.body.language;
}
if (req.body.status) where.status = { $like: req.body.status + '%' };
if (req.body.problem_id) where.problem_id = parseInt(req.body.problem_id) || -1;
let count = await JudgeState.count(where);
if (req.body.type === 'rejudge') {
let submissions = await JudgeState.query(null, where);
for (let submission of submissions) {
await submission.rejudge();
}
}
res.render('admin_rejudge', {
form: req.body,
count: count
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.get('/admin/links', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
res.render('admin_links', {
links: syzoj.config.links || []
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.post('/admin/links', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
syzoj.config.links = JSON.parse(req.body.data);
await syzoj.utils.saveConfig();
res.redirect(syzoj.utils.makeUrl(['admin', 'links']));
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.get('/admin/raw', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
res.render('admin_raw', {
data: JSON.stringify(syzoj.config, null, 2)
});
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});
app.post('/admin/raw', async (req, res) => {
try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
syzoj.config = JSON.parse(req.body.data);
await syzoj.utils.saveConfig();
res.redirect(syzoj.utils.makeUrl(['admin', 'raw']));
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
})
}
});

17
static/script.js

@ -2,8 +2,21 @@ var addUrlParam = function (url, key, val) {
var newParam = encodeURIComponent(key) + '=' + encodeURIComponent(val);
url = url.split('#')[0];
if (url.indexOf('?') === -1) url += '?' + newParam;
else url += '&' + newParam;
var twoPart = url.split('?'), params = {};
var tmp = twoPart[1] ? twoPart[1].split('&') : [];
for (let i in tmp) {
let a = tmp[i].split('=');
params[a[0]] = a[1];
}
params[key] = val;
url = twoPart[0] + '?';
for (let key in params) {
url += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';
}
url = url.substring(0, url.length - 1);
return url;
};

15
utility.js

@ -173,8 +173,15 @@ module.exports = {
parseDate(s) {
return parseInt(+new Date(s) / 1000);
},
getCurrentDate() {
return parseInt(+new Date / 1000);
getCurrentDate(removeTime) {
let d = new Date;
if (removeTime) {
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
}
return parseInt(+d / 1000);
},
makeUrl(req_params, form) {
let res = '';
@ -348,5 +355,9 @@ module.exports = {
} catch (e) {
return false;
}
},
async saveConfig() {
let fs = require('fs-extra');
fs.writeFileAsync(syzoj.rootDir + '/config.json', JSON.stringify(syzoj.config, null, 2));
}
};

25
views/admin.ejs

@ -1,25 +0,0 @@
<% include header %>
<div class="ui grid">
<div class="four wide column">
<div class="ui vertical fluid tabular menu">
<a class="item">
;
</a>
<a class="item">
Pics
</a>
<a class="item">
Companies
</a>
<a class="item active">
Links
</a>
</div>
</div>
<div class="twelve wide stretched column">
<div class="ui segment">
This is an stretched grid column. This segment will always match the tab height
</div>
</div>
</div>
<% include footer %>

54
views/admin_config.ejs

@ -0,0 +1,54 @@
<% this.adminPage = 'config'; %>
<% include admin_header %>
<form method="post" class="ui form">
<%
function showTitle(title) {
%><h4 class="ui dividing header"><%= title %></h4><%
}
function showStringItem(name, text, val) {
%>
<div class="ui fluid input inline field">
<label style="line-height: 37.8px; font-weight: normal; "><%= text %></label>
<input type="text" name="<%= name %>" value="<%= val %>">
</div>
<%
}
function showNumberItem(name, text, val) {
%>
<div class="ui fluid input inline field">
<label style="line-height: 37.8px; font-weight: normal; "><%= text %></label>
<input type="number" name="<%= name %>" value="<%= val %>">
</div>
<%
}
function showBooleanItem(name, text, val) {
%>
<div class="inline field">
<div class="ui toggle checkbox">
<input name="<%= name %>" type="checkbox" class=""<% if (val) { %> checked<% } %>>
<label><%= text %></label>
</div>
</div>
<%
}
for (let item in items) {
if (items[item] === null) {
showTitle(item);
} else if (items[item].type === String) {
showStringItem(item, items[item].name, items[item].val);
} else if (items[item].type === Number) {
showNumberItem(item, items[item].name, items[item].val);
} else {
showBooleanItem(item, items[item].name, items[item].val);
}
}
%>
<div style="text-align: center; ">
<button class="ui blue button">提交</button>
</div>
</form>
<% include admin_footer %>

3
views/admin_footer.ejs

@ -0,0 +1,3 @@
</div>
</div>
<% include footer %>

24
views/admin_header.ejs

@ -0,0 +1,24 @@
<%
let items = {
info: '统计信息',
config: '系统配置',
privilege: '权限管理',
rejudge: '一键重测',
links: '友链管理',
raw: '配置文件'
};
%>
<% this.title = items[this.adminPage] + ' - 后台管理'; %>
<% include header %>
<h1 style="text-align: center; margin-bottom: 27px; ">后台管理</h1>
<div class="ui grid">
<div class="three wide column">
<div class="ui vertical fluid tabular menu" style="height: 100%; ">
<% for (let x in items) { %>
<a class="item<% if (this.adminPage === x) { %> active<% } %>"<% if (this.adminPage !== x) { %> href="<%= syzoj.utils.makeUrl(['admin', x]) %>"<% } %>>
<%= items[x] %>
</a>
<% } %>
</div>
</div>
<div class="thirteen wide stretched column">

79
views/admin_info.ejs

@ -0,0 +1,79 @@
<% this.adminPage = 'info'; %>
<% include admin_header %>
<style>
.ui.statistics {
width: 100%;
}
.statistic {
width: calc(100% / 3 - 3em);
}
.statistic .icon {
font-size: 0.7em;
display: inline-block;
vertical-align: top;
padding-top: 2px;
}
.statistic .label {
font-weight: normal !important;
}
</style>
<div class="ui statistics">
<div class="statistic">
<div class="value">
<i class="tasks icon"></i>
<%= allSubmissionsCount %>
</div>
<div class="label">
总评测量
</div>
</div>
<div class="statistic">
<div class="value">
<i class="tasks icon"></i>
<%= todaySubmissionsCount %>
</div>
<div class="label">
今日评测量
</div>
</div>
<div class="statistic">
<div class="value">
<i class="list icon"></i>
<%= problemsCount %>
</div>
<div class="label">
题目数量
</div>
</div>
<div class="statistic">
<div class="value">
<i class="comments icon"></i>
<%= articlesCount %>
</div>
<div class="label">
讨论数量
</div>
</div>
<div class="statistic">
<div class="value">
<i class="calendar icon"></i>
<%= contestsCount %>
</div>
<div class="label">
比赛数量
</div>
</div>
<div class="statistic">
<div class="value">
<i class="user icon"></i>
<%= usersCount %>
</div>
<div class="label">
用户数量
</div>
</div>
</div>
<% include admin_footer %>

78
views/admin_links.ejs

@ -0,0 +1,78 @@
<%
this.adminPage = 'links';
%>
<% include admin_header %>
<table class="ui center aligned celled table">
<thead>
<tr>
<th>名称</th>
<th>链接</th>
<th width="50px">删除</th>
</tr>
</thead>
<tbody>
<% for (let i = 0; i < links.length; i++) { %>
<tr>
<td><%= links[i].title %></td>
<td><a href="<%= links[i].url %>"><%= links[i].url %></a></td>
<td>
<a onclick="$('#modal-remove-<%= i %>').modal('show');" style="color: #000; " href="#"><i class="remove icon"></i></a>
<div class="ui basic modal" id="modal-remove-<%= i %>">
<div class="ui icon header">
<i class="remove icon"></i>
<p style="margin-top: 15px; ">删除友链</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认删除该友链吗?</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button remove_link" data-id="<%= i %>">
<i class="checkmark icon"></i>
</a>
</div>
</div>
</td>
</tr>
<% } %>
</tbody>
</table>
<div class="ui form">
<div class="two fields">
<div class="field">
<label>名称</label>
<input type="text" id="add_title">
</div>
<div class="field">
<label>链接</label>
<input type="text" id="add_url">
</div>
</div>
<div style="text-align: center; "><div id="add_button" class="ui submit button">添加</a></div>
</div>
<form method="post" id="submit_form">
<input type="hidden" name="data" id="submit_data">
</form>
<script>
var links = <%- JSON.stringify(links) %>;
$('.remove_link').click(function () {
links.splice($(this).data('id'), 1);
$('#submit_data').val(JSON.stringify(links));
$('#submit_form').submit();
});
$('#add_button').click(function () {
links.push({ title: $('#add_title').val(), url: $('#add_url').val() });
$('#submit_data').val(JSON.stringify(links));
$('#submit_form').submit();
});
</script>
<% include admin_footer %>

63
views/admin_privilege.ejs

@ -0,0 +1,63 @@
<%
this.adminPage = 'privilege';
let privileges = {
manage_problem: '管理题目',
manage_problem_tag: '管理题目标签',
manage_user: '管理用户',
};
%>
<% include admin_header %>
<table class="ui center aligned celled table">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<% for (let privilege in privileges) { %>
<th width="20%"><%= privileges[privilege] %></th>
<% } %>
</tr>
</thead>
<tbody>
<% for (let user of users) { %>
<tr>
<td><%= user.user.id %></td>
<td><%= user.user.username %></td>
<% for (let privilege in privileges) { %>
<td>
<div class="ui fitted toggle checkbox checkbox_privilege" data-user="<%= user.user.id %>" data-name="<%= privilege %>">
<input type="checkbox"<% if (user.privileges.includes(privilege)) { %> checked<% } %>>
<label></label>
</div>
</td>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<div style="text-align: center; ">
<button class="ui button" onclick="submit()">提交</button>
</div>
<form method="post" id="submit_form">
<input type="hidden" name="data" id="submit_data">
</form>
<script>
function submit() {
var users = {};
$('.checkbox_privilege').each(function () {
var user = $(this).data('user');
var name = $(this).data('name');
if (!users[user]) users[user] = [];
if ($(this).checkbox('is checked')) {
users[user].push(name);
}
});
$('#submit_data').val(JSON.stringify(users));
$('#submit_form').submit();
}
</script>
<% include admin_footer %>

34
views/admin_raw.ejs

@ -0,0 +1,34 @@
<% this.adminPage = 'raw'; %>
<% include admin_header %>
<form method="post" class="ui form">
<div id="editor" style="border: 1px solid #D4D4D5; height: 500px; margin-bottom: 20px; "><%= data %></div>
<input type="hidden" name="data">
<script src="/libs/ace/ace.js"></script>
<script type="text/javascript">
var editor = ace.edit("editor");
editor.setTheme("ace/theme/tomorrow");
editor.getSession().setMode("ace/mode/json");
editor.getSession().setUseSoftTabs(false);
editor.container.name = 'data';
editor.container.style.lineHeight = 1.6;
editor.container.style.fontSize = '14px';
editor.container.style.fontFamily = "'Roboto Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', monospace";
editor.setShowPrintMargin(false);
editor.renderer.updateFontSize();
var input = $('[name="data"]');
editor.getSession().on("change", function () {
input.val(editor.getSession().getValue());
});
</script>
<div style="text-align: center; ">
<button class="ui blue button">提交</button>
</div>
</form>
<% include admin_footer %>

108
views/admin_rejudge.ejs

@ -0,0 +1,108 @@
<% this.adminPage = 'rejudge'; %>
<% include admin_header %>
<form method="post" class="ui form">
<div class="four fields">
<div class="field">
<label>题目 ID</label>
<input type="text" name="problem_id" placeholder="题目 ID" value="<%= form.problem_id %>">
</div>
<div class="field">
<label>用户名</label>
<input type="text" name="submitter" placeholder="用户名" value="<%= form.submitter %>">
</div>
<div class="field">
<label>语言</label>
<div class="ui selection dropdown">
<input type="hidden" name="language" value="<%= form.language %>">
<i class="dropdown icon"></i>
<div class="default text"></div>
<div class="menu">
<div class="item" data-value="">不限</div>
<div class="item" data-value="submit-answer">提交答案</div>
<% for (let lang in syzoj.config.languages) { %>
<div class="item" data-value="<%= lang %>"><%= syzoj.config.languages[lang].show %></div>
<% } %>
</div>
</div>
</div>
<div class="field">
<label>状态</label>
<div class="ui selection dropdown">
<input type="hidden" name="status" value="<%= form.status %>">
<i class="dropdown icon"></i>
<div class="default text"></div>
<div class="menu">
<div class="item" data-value="">不限<i class="dropdown icon" style="visibility: hidden; "></i></div>
<% for (let status in this.icon) { %>
<% 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>
</div>
</div>
</div>
<div class="three fields">
<div class="field">
<label>ID(最小值)</label>
<input type="text" name="min_id" placeholder="ID(最小值)" value="<%= form.min_id %>">
</div>
<div class="field">
<label>分数(最小值)</label>
<input type="text" name="min_score" placeholder="分数(最小值)" value="<%= form.min_score %>">
</div>
<div class="field">
<label>提交时间(最小值)</label>
<input type="text" name="min_time" placeholder="提交时间(最小值)" value="<%= form.min_time || syzoj.utils.formatDate(0) %>">
</div>
</div>
<div class="three fields">
<div class="field">
<label>ID(最大值)</label>
<input type="text" name="max_id" placeholder="ID(最大值)" value="<%= form.max_id %>">
</div>
<div class="field">
<label>分数(最大值)</label>
<input type="text" name="max_score" placeholder="分数(最大值)" value="<%= form.max_score %>">
</div>
<div class="field">
<label>提交时间(最大值)</label>
<input type="text" name="max_time" placeholder="提交时间(最大值)" value="<%= form.max_time || syzoj.utils.formatDate(2147483647) %>">
</div>
</div>
<div style="text-align: center; ">
<button class="ui button" name="type" value="query">查询</button>
<% if (form.type === 'rejudge') { %>
<button class="ui blue disabled button" name="type" value="rejudge">已重测 <%= count %> 条记录</button>
<% } else if (count !== null) { %>
<a onclick="$('#modal-rejudge').modal('show');" class="ui blue<% if (count === 0) { %> disabled<% } %> button"><% if (count === 0) { %>没有符合条件的记录<% } else { %>重测 <%= count %> 条记录<% } %></a>
<button id="submit_rejudge" name="type" value="rejudge" style="display: none; "></button>
<div class="ui basic modal" id="modal-rejudge">
<div class="ui icon header">
<i class="refresh icon"></i>
<p style="margin-top: 15px; ">重新评测</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认重新评测 <%= count %> 条记录吗?</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<button class="ui green ok inverted button" onclick="$('#submit_rejudge').click()">
<i class="checkmark icon"></i>
</button>
</div>
</div>
<% } %>
</div>
</form>
<script>
$(function () {
$('.ui.dropdown').dropdown();
});
</script>
<% include admin_footer %>

3
views/header.ejs

@ -34,6 +34,9 @@
<%= user.username %><% if (user.nameplate) { %><%- user.nameplate %><% } %> <i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="<%= syzoj.utils.makeUrl(['user', user.id, 'edit']) %>"><i class="edit icon"></i>修改资料</a>
<% if (user.is_admin) { %>
<a class="item" href="<%= syzoj.utils.makeUrl(['admin', 'info']) %>"><i class="settings icon"></i>后台管理</a>
<% } %>
<a class="item" href-post="<%= syzoj.utils.makeUrl(['logout'], { url: req.originalUrl }) %>"><i class="power icon"></i>注销</a>
</div>
</div>

Loading…
Cancel
Save