34 changed files with 1293 additions and 490 deletions
@ -0,0 +1,115 @@
|
||||
/* |
||||
* 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/>.
|
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('file', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
type: { type: Sequelize.STRING(80) }, |
||||
md5: { type: Sequelize.STRING(80), unique: true } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'file', |
||||
indexes: [ |
||||
{ |
||||
fields: ['type'], |
||||
}, |
||||
{ |
||||
fields: ['md5'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class File extends Model { |
||||
static create(val) { |
||||
return File.fromRecord(File.model.build(Object.assign({ |
||||
type: '', |
||||
md5: '' |
||||
}, val))); |
||||
} |
||||
|
||||
getPath() { |
||||
return File.resolvePath(this.type, this.md5); |
||||
} |
||||
|
||||
static resolvePath(type, md5) { |
||||
return syzoj.utils.resolvePath(syzoj.config.upload_dir, type, md5); |
||||
} |
||||
|
||||
static async upload(path, type) { |
||||
let fs = Promise.promisifyAll(require('fs-extra')); |
||||
|
||||
let buf = await fs.readFileAsync(path); |
||||
|
||||
if (buf.length > syzoj.config.limit.data_size) throw new ErrorMessage('数据包太大。'); |
||||
|
||||
try { |
||||
let AdmZip = require('adm-zip'); |
||||
let zip = new AdmZip(buf); |
||||
this.unzipSize = 0; |
||||
for (let x of zip.getEntries()) this.unzipSize += x.header.size; |
||||
} catch (e) { |
||||
this.unzipSize = null; |
||||
} |
||||
|
||||
let key = syzoj.utils.md5(buf); |
||||
await fs.moveAsync(path, File.resolvePath(type, key), { overwrite: true }); |
||||
|
||||
let file = await File.findOne({ where: { md5: key } }); |
||||
if (!file) { |
||||
file = await File.create({ |
||||
type: type, |
||||
md5: key |
||||
}); |
||||
await file.save(); |
||||
} |
||||
|
||||
return file; |
||||
} |
||||
|
||||
async getUnzipSize() { |
||||
if (this.unzipSize === undefined) { |
||||
try { |
||||
let fs = Promise.promisifyAll(require('fs-extra')); |
||||
let buf = await fs.readFileAsync(this.getPath()); |
||||
|
||||
let AdmZip = require('adm-zip'); |
||||
let zip = new AdmZip(buf); |
||||
|
||||
this.unzipSize = 0; |
||||
for (let x of zip.getEntries()) this.unzipSize += x.header.size; |
||||
} catch (e) { |
||||
this.unzipSize = null; |
||||
} |
||||
} |
||||
|
||||
if (this.unzipSize === null) throw new ErrorMessage('无效的 ZIP 文件。'); |
||||
else return this.unzipSize; |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
File.model = model; |
||||
|
||||
module.exports = File; |
@ -1,64 +0,0 @@
|
||||
/* |
||||
* 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/>.
|
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('file', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
filename: { type: Sequelize.STRING(80), unique: true }, |
||||
md5: { type: Sequelize.STRING(80), unique: true } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'file', |
||||
indexes: [ |
||||
{ |
||||
fields: ['filename'], |
||||
}, |
||||
{ |
||||
fields: ['md5'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class TestData extends Model { |
||||
static create(val) { |
||||
return TestData.fromRecord(TestData.model.build(Object.assign({ |
||||
filename: '', |
||||
md5: '' |
||||
}, val))); |
||||
} |
||||
|
||||
getPath() { |
||||
return TestData.resolvePath(this.md5); |
||||
} |
||||
|
||||
static resolvePath(md5) { |
||||
return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata', md5); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
TestData.model = model; |
||||
|
||||
module.exports = TestData; |
@ -1,151 +1,164 @@
|
||||
<% this.title = '上传测试数据'; %> |
||||
<% include header %> |
||||
<% |
||||
let subtaskType = { |
||||
sum: '测试点分数按百分比相加', |
||||
min: '取各测试点最低分', |
||||
mul: '测试点分数按百分比相乘' |
||||
}; |
||||
this.title = '测试数据'; |
||||
function getIcon(filename) { |
||||
let a = { |
||||
'.cpp': 'file code outline', |
||||
'.c': 'file code outline', |
||||
'.cs': 'file code outline', |
||||
'.pas': 'file code outline', |
||||
'.py': 'file code outline', |
||||
'.js': 'file code outline', |
||||
'.java': 'file code outline', |
||||
'.hs': 'file code outline', |
||||
'.vala': 'file code outline', |
||||
'.lua': 'file code outline', |
||||
'.rb': 'file code outline', |
||||
'.vb': 'file code outline', |
||||
'.ml': 'file code outline', |
||||
'.in': 'file text outline', |
||||
'.out': 'file text outline', |
||||
'.ans': 'file text outline', |
||||
'.txt': 'file text outline', |
||||
'.md': 'file text outline', |
||||
'.md': 'file text outline', |
||||
'.docx': 'file word outline', |
||||
'.odt': 'file word outline', |
||||
'.xlsx': 'file excel outline', |
||||
'.ods': 'file excel outline', |
||||
'.pptx': 'file powerpoint outline', |
||||
'.odp': 'file powerpoint outline', |
||||
'.zip': 'file archive outline', |
||||
'.7z': 'file archive outline', |
||||
} |
||||
for (let x in a) if (filename.endsWith(x)) return a[x]; |
||||
return 'file outline'; |
||||
} |
||||
%> |
||||
<% include header %> |
||||
<div class="padding"> |
||||
<div class="ui grid"> |
||||
<div class="row"> |
||||
<div class="seven wide column"> |
||||
<% if (problem.testdata) { %> |
||||
<% |
||||
try { |
||||
let list = syzoj.utils.parseTestData(problem.testdata.getPath()); |
||||
%> |
||||
<% if (list.spj) { %> |
||||
<p>评测方式:Special Judge</p> |
||||
<% } else { %> |
||||
<p>评测方式:文本比较</p> |
||||
<% } %> |
||||
<table class="ui celled table"> |
||||
<tbody> |
||||
<% let i = 0; %> |
||||
<% for (let subtask of list) { %> |
||||
<% if (list.length !== 1) { %> |
||||
<tr> |
||||
<td style="background-color: #F9FAFB" colspan="2"><h4 style="margin-bottom: 3px; ">子任务 <%= ++i %></h4><span style="font-weight: normal; "><%= subtaskType[subtask.type] %>,总分值 <%= subtask.score %></span></th> |
||||
</tr> |
||||
<% } else { %> |
||||
<tr> |
||||
<td style="background-color: #F9FAFB" colspan="2"><h4 style="margin-bottom: 3px; ">单个子任务</h4><span style="font-weight: normal; "><%= subtaskType[subtask.type] %></span></th> |
||||
</tr> |
||||
<% } %> |
||||
<% for (let testcase of subtask.cases) { %> |
||||
<tr> |
||||
<td><%= testcase.input %></td> |
||||
<td><%= testcase.output %></td> |
||||
</tr> |
||||
<% } %> |
||||
<% } %> |
||||
</tbody> |
||||
</table> |
||||
<% } catch (e) { %> |
||||
<h3>数据包错误:<%= e %></h3> |
||||
<div class="ui grid"> |
||||
<div class="seven wide column"> |
||||
<h3 style="text-align: center; ">测试点信息</h3> |
||||
<% include problem_testcases %> |
||||
</div> |
||||
<div class="nine wide column"> |
||||
<h3 style="text-align: center; ">文件列表</h3> |
||||
<% if (testdata) { %> |
||||
<table class="ui very basic center aligned table"> |
||||
<thead> |
||||
<tr> |
||||
<th class="left aligned">文件名</th> |
||||
<th style="width: 100px">文件大小</th> |
||||
<th style="width: 35px">下载</th> |
||||
<% if (problem.allowedEdit) { %> |
||||
<th style="width: 35px">删除</th> |
||||
<% } %> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<% if (testdata.zip) { %> |
||||
<tr> |
||||
<td class="left aligned"><i class="file archive outline icon"></i> 完整数据包</td> |
||||
<td><%- !testdata.zip.size ? '<i class="minus icon"></i>' : syzoj.utils.formatSize(testdata.zip.size) %></td> |
||||
<td><a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download']) %>"><i class="download icon"></i></td> |
||||
<% if (problem.allowedEdit) { %> |
||||
<td><i class="minus icon"></i></td> |
||||
<% } %> |
||||
</tr> |
||||
<% } %> |
||||
<% } else { %> |
||||
<h3>数据未上传</h3> |
||||
<% } %> |
||||
</div> |
||||
<div class="nine wide column"> |
||||
<form class="ui form" method="post" enctype="multipart/form-data"> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="doc-ds-ipt-1">时间限制(单位: ms)</label> |
||||
<input type="number" name="time_limit" value="<%= problem.time_limit %>"> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="doc-ds-ipt-1">内存限制(单位: MiB)</label> |
||||
<input type="number" name="memory_limit" value="<%= problem.memory_limit %>"> |
||||
</div> |
||||
</div> |
||||
<% if (!problem.file_io) { %> |
||||
<% let i = 0; %> |
||||
<% if (testdata.files) for (let file of testdata.files) { %> |
||||
<% i++; %> |
||||
<tr> |
||||
<td class="left aligned"><i class="<%= getIcon(file.filename) %> icon"></i> <%= file.filename %></td> |
||||
<td><%= syzoj.utils.formatSize(file.size) %></td> |
||||
<td> |
||||
<a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download', file.filename]) %>"> |
||||
<i class="download icon"></i> |
||||
</a> |
||||
</td> |
||||
<% if (problem.allowedEdit) { %> |
||||
<td> |
||||
<a style="color: #000; " onclick="$('#modal-delete-<%= i %>').modal('show')"> |
||||
<i class="remove icon"></i> |
||||
</a> |
||||
<div class="ui basic modal" id="modal-delete-<%= i %>"> |
||||
<div class="ui icon header"> |
||||
<i class="trash icon"></i> |
||||
<p style="margin-top: 15px; ">删除文件</p> |
||||
</div> |
||||
<div class="content" style="text-align: center; "> |
||||
<p>确认删除「 <samp><%= file.filename %></samp> 」吗?</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" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'delete', file.filename]) %>"> |
||||
<i class="checkmark icon"></i> |
||||
是 |
||||
</a> |
||||
</div> |
||||
</div> |
||||
</td> |
||||
<% } %> |
||||
</tr> |
||||
<% } %> |
||||
</tbody> |
||||
</table> |
||||
<% } else { %> |
||||
<h4 style="text-align: center; ">无测试数据</h4> |
||||
<% } %> |
||||
<% if (problem.allowedEdit) { %> |
||||
<form id="form_upload" class="ui form" action="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'upload']) %>" method="post" enctype="multipart/form-data"> |
||||
<div class="inline fields"> |
||||
<label>IO 方式</label> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()" checked> |
||||
<label for="std-io">标准 IO</label> |
||||
</div> |
||||
</div> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()"> |
||||
<label for="file-io">文件 IO</label> |
||||
<div class="field" style="margin: 0 auto; "> |
||||
<label for="answer">上传文件(可一次性上传多个)</label> |
||||
<input type="file" name="file" multiple id="upload_file"> |
||||
<div class="ui center aligned vertical segment" style="padding-bottom: 0; "> |
||||
<div class="ui button" onclick="check_replace()">提交</div> |
||||
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="file_io_input_name">输入文件名</label> |
||||
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>" disabled> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="file_io_output_name">输出文件名</label> |
||||
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>" disabled> |
||||
</div> |
||||
</form> |
||||
<div class="ui basic modal" id="modal-replace"> |
||||
<div class="ui icon header"> |
||||
<i class="refresh icon"></i> |
||||
<p style="margin-top: 15px; ">替换文件</p> |
||||
</div> |
||||
<% } else { %> |
||||
<div class="inline fields"> |
||||
<label>IO 方式</label> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()"> |
||||
<label for="std-io">标准 IO</label> |
||||
</div> |
||||
</div> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()" checked> |
||||
<label for="file-io">文件 IO</label> |
||||
</div> |
||||
</div> |
||||
<div class="content" style="text-align: center; "> |
||||
<p>确认替换以下文件吗?</p> |
||||
<div style="display: inline-block; text-align: left; " id="replaced_files"></div> |
||||
</div> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="file_io_input_name">输入文件名</label> |
||||
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>"> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="file_io_output_name">输出文件名</label> |
||||
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>"> |
||||
<div class="actions"> |
||||
<div class="ui red basic cancel inverted button"> |
||||
<i class="remove icon"></i> |
||||
否 |
||||
</div> |
||||
<a class="ui green ok inverted button" onclick="$('#form_upload').submit()"> |
||||
<i class="checkmark icon"></i> |
||||
是 |
||||
</a> |
||||
</div> |
||||
<% } %> |
||||
<div class="field"> |
||||
<label for="testdata"><% if (!problem.testdata_id) { %>上传测试数据<% } else { %>更新测试数据<% } %></label> |
||||
<input type="file" id="testdata" name="testdata"> |
||||
</div> |
||||
<button type="submit" class="ui button">提交</button> |
||||
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
<script> |
||||
function check_replace() { |
||||
var old_files = <%- JSON.stringify((testdata && testdata.files ? testdata.files : []).map(x => x.filename)) %>; |
||||
var replaced_files = Array.from($('#upload_file')[0].files).map(function (x) { return x.name; }).filter(function (x) { return old_files.includes(x); }); |
||||
var s = ''; |
||||
for (let file of replaced_files) s += '<samp>' + file + '</samp><br>'; |
||||
if (s) { |
||||
$('#replaced_files').html(s); |
||||
$('#modal-replace').modal('show'); |
||||
} else { |
||||
$('#form_upload').submit(); |
||||
} |
||||
} |
||||
</script> |
||||
<% } %> |
||||
</div> |
||||
</div> |
||||
<div> |
||||
<script> |
||||
function goEnable() { |
||||
document.getElementById('file-io-input-name').disabled = false; |
||||
document.getElementById('file-io-output-name').disabled = false; |
||||
} |
||||
function goDisable() { |
||||
document.getElementById('file-io-input-name').disabled = true; |
||||
document.getElementById('file-io-output-name').disabled = true; |
||||
} |
||||
|
||||
$(document).ready(function () { |
||||
$('#file-io-input-name').on('input keyup change', function (e) { |
||||
var prob = $('#file-io-input-name').val(); |
||||
if (prob.lastIndexOf('.') !== -1) prob = prob.substring(0, prob.lastIndexOf('.')); |
||||
$('#file-io-output-name').attr('placeholder', prob + '.out'); |
||||
}); |
||||
$('#file-io-output-name').focus(function (e) { |
||||
if (!$('#file-io-output-name').val()) { |
||||
$('#file-io-output-name').val($('#file-io-output-name').attr('placeholder')); |
||||
} |
||||
}); |
||||
}); |
||||
</script> |
||||
<% include footer %> |
||||
</div> |
||||
<% include footer %> |
@ -0,0 +1,144 @@
|
||||
<% this.title = '管理题目'; %> |
||||
<% include header %> |
||||
<div class="padding"> |
||||
<div class="ui grid"> |
||||
<div class="row"> |
||||
<div class="seven wide column"> |
||||
<% include problem_testcases %> |
||||
</div> |
||||
<div class="nine wide column"> |
||||
<form class="ui form" method="post" enctype="multipart/form-data" onsubmit="return checkSubmit()"> |
||||
<input type="hidden" name="type" value="<%= problem.type %>"> |
||||
<div class="ui pointing secondary menu" id="problem-type-tab" style="margin-top: -10px; "> |
||||
<a class="<%= problem.type === 'traditional' ? 'active ' : '' %>item" data-tab="traditional">传统</a> |
||||
<a class="<%= problem.type === 'interaction' ? 'active ' : '' %>item" data-tab="interaction">交互</a> |
||||
<a class="<%= problem.type === 'submit-answer' ? 'active ' : '' %>item" data-tab="submit-answer">提交答案</a> |
||||
</div> |
||||
<div class="ui <%= problem.type !== 'submit-answer' ? 'active ' : '' %>tab" data-tab="traditional" data-tab="interaction"> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="doc-ds-ipt-1">时间限制(单位: ms)</label> |
||||
<input type="number" name="time_limit" value="<%= problem.time_limit %>"> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="doc-ds-ipt-1">内存限制(单位: MiB)</label> |
||||
<input type="number" name="memory_limit" value="<%= problem.memory_limit %>"> |
||||
</div> |
||||
</div> |
||||
<% if (!problem.file_io) { %> |
||||
<div class="inline fields"> |
||||
<label>IO 方式</label> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()" checked> |
||||
<label for="std-io">标准 IO</label> |
||||
</div> |
||||
</div> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()"> |
||||
<label for="file-io">文件 IO</label> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="file_io_input_name">输入文件名</label> |
||||
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>" disabled> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="file_io_output_name">输出文件名</label> |
||||
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>" disabled> |
||||
</div> |
||||
</div> |
||||
<% } else { %> |
||||
<div class="inline fields"> |
||||
<label>IO 方式</label> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()"> |
||||
<label for="std-io">标准 IO</label> |
||||
</div> |
||||
</div> |
||||
<div class="field"> |
||||
<div class="ui radio checkbox"> |
||||
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()" checked> |
||||
<label for="file-io">文件 IO</label> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="two fields"> |
||||
<div class="field"> |
||||
<label for="file_io_input_name">输入文件名</label> |
||||
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>"> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="file_io_output_name">输出文件名</label> |
||||
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>"> |
||||
</div> |
||||
</div> |
||||
<% } %> |
||||
</div> |
||||
<div class="ui <%= problem.type === 'submit-answer' ? 'active ' : '' %>tab" data-tab="submit-answer" style="margin-bottom: 10px; "> |
||||
<b>为了避免系统出错,已有提交的题目不允许在提交答案和非提交答案之间更改。</b><br> |
||||
提交答案题目不需要设置时间限制、空间限制以及 IO 方式。<br> |
||||
提交时使用的答案文件名需要与测试数据中每个测试点输出文件相同,且后缀名为 <code>.out</code>。 |
||||
</div> |
||||
<div class="field"> |
||||
<label for="testdata">上传测试数据(请使用 ZIP 格式)</label> |
||||
<input type="file" id="testdata" name="testdata"> |
||||
</div> |
||||
<div class="field"> |
||||
<label for="additional_file">上传附加文件(请使用 ZIP 格式)</label> |
||||
<input type="file" id="additional_file" name="additional_file"> |
||||
</div> |
||||
<button type="submit" class="ui button">提交</button> |
||||
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
<div> |
||||
<script> |
||||
function goEnable() { |
||||
document.getElementById('file-io-input-name').disabled = false; |
||||
document.getElementById('file-io-output-name').disabled = false; |
||||
} |
||||
function goDisable() { |
||||
document.getElementById('file-io-input-name').disabled = true; |
||||
document.getElementById('file-io-output-name').disabled = true; |
||||
} |
||||
|
||||
$(function () { |
||||
$('#file-io-input-name').on('input keyup change', function (e) { |
||||
var prob = $('#file-io-input-name').val(); |
||||
if (prob.lastIndexOf('.') !== -1) prob = prob.substring(0, prob.lastIndexOf('.')); |
||||
$('#file-io-output-name').attr('placeholder', prob + '.out'); |
||||
}); |
||||
$('#file-io-output-name').focus(function (e) { |
||||
if (!$('#file-io-output-name').val()) { |
||||
$('#file-io-output-name').val($('#file-io-output-name').attr('placeholder')); |
||||
} |
||||
}); |
||||
|
||||
$('#problem-type-tab .item').tab(); |
||||
|
||||
$('a[data-tab="traditional"]').click(function () { |
||||
$('input[name=type]').val('traditional'); |
||||
if ($('div[data-tab="interaction"]').attr('data-tab', 'traditional').length) $('a[data-tab="traditional"]').click(); |
||||
}); |
||||
|
||||
$('a[data-tab="interaction"]').click(function () { |
||||
$('input[name=type]').val('interaction'); |
||||
if ($('div[data-tab="traditional"]').attr('data-tab', 'interaction').length) $('a[data-tab="interaction"]').click(); |
||||
}); |
||||
|
||||
$('a[data-tab="submit-answer"]').click(function () { |
||||
$('input[name=type]').val('submit-answer'); |
||||
}); |
||||
}); |
||||
|
||||
function checkSubmit() { |
||||
; |
||||
} |
||||
</script> |
||||
<% include footer %> |
@ -0,0 +1,42 @@
|
||||
<% |
||||
let subtaskType = { |
||||
sum: '测试点分数按百分比相加', |
||||
min: '取各测试点最低分', |
||||
mul: '测试点分数按百分比相乘' |
||||
}; |
||||
%> |
||||
<% if (testcases && testcases.error) { %> |
||||
<h4>数据包错误:<%= testcases.error %></h4> |
||||
<% |
||||
} else if (testcases) { |
||||
%> |
||||
<% if (testcases.spj) { %> |
||||
<p>评测方式:Special Judge</p> |
||||
<% } else { %> |
||||
<p>评测方式:文本比较</p> |
||||
<% } %> |
||||
<table class="ui celled table"> |
||||
<tbody> |
||||
<% let i = 0; %> |
||||
<% for (let subtask of testcases) { %> |
||||
<% if (testcases.length !== 1) { %> |
||||
<tr> |
||||
<td style="background-color: #F9FAFB" colspan="2"><h4 style="margin-bottom: 3px; ">子任务 <%= ++i %></h4><span style="font-weight: normal; "><%= subtaskType[subtask.type] %>,总分值 <%= subtask.score %></span></th> |
||||
</tr> |
||||
<% } else { %> |
||||
<tr> |
||||
<td style="background-color: #F9FAFB" colspan="2"><h4 style="margin-bottom: 3px; ">单个子任务</h4><span style="font-weight: normal; "><%= subtaskType[subtask.type] %></span></th> |
||||
</tr> |
||||
<% } %> |
||||
<% for (let testcase of subtask.cases) { %> |
||||
<tr class="center aligned"> |
||||
<td style="width: 50%; "><%= testcase.input %></td> |
||||
<td style="width: 50%; "><%= testcase.output %></td> |
||||
</tr> |
||||
<% } %> |
||||
<% } %> |
||||
</tbody> |
||||
</table> |
||||
<% } else { %> |
||||
<h4 style="text-align: center; ">无测试数据</h4> |
||||
<% } %> |
Loading…
Reference in new issue