Browse Source

Merge branch 'master' of zhaojunzhe/fair-web into master

pull/31/head
richie 5 years ago committed by Gogs
parent
commit
feee786a0b
  1. 29
      libs/submissions_process.js
  2. 14
      modules/contest.js
  3. 5
      modules/submission.js
  4. 2
      views/admin_classify.ejs
  5. 129
      views/contest_edit.ejs
  6. 4
      views/header.ejs
  7. 130
      views/problem_table.ejs
  8. 69
      views/submissions.ejs
  9. 1
      views/submissions_item.ejs

29
libs/submissions_process.js

@ -1,19 +1,22 @@
const {getCachedJudgeState} = require('./judger'); const {getCachedJudgeState} = require('./judger');
const _ = require('lodash'); const _ = require('lodash');
const getSubmissionInfo = (s, displayConfig) => ({ const getSubmissionInfo = (s, displayConfig) => {
submissionId: s.id, return {
taskId: s.task_id, submissionId: s.id,
user: s.user.username, taskId: s.task_id,
userId: s.user_id, user: s.user.username,
problemName: s.problem.title, userId: s.user_id,
problemId: s.problem_id, problemName: s.problem.title,
language: displayConfig.showCode ? ((s.language != null && s.language !== '') ? syzoj.languages[s.language].show : null) : null, problemId: s.problem_id,
codeSize: displayConfig.showCode ? s.code_length : null, language: displayConfig.showCode ? ((s.language != null && s.language !== '') ? syzoj.languages[s.language].show : null) : null,
submitTime: syzoj.utils.formatDate(s.submit_time), codeSize: displayConfig.showCode ? s.code_length : null,
isPractice: s.is_practice, submitTime: syzoj.utils.formatDate(s.submit_time),
c_id: s.c_id isPractice: s.is_practice,
}); c_id: s.c_id,
is_share: s.is_share || false
}
};
const getRoughResult = (x, displayConfig, roughOnly) => { const getRoughResult = (x, displayConfig, roughOnly) => {
let max = 0; let max = 0;

14
modules/contest.js

@ -53,6 +53,7 @@ app.get('/contest/:id/edit', async (req, res) => {
res.render('contest_edit', { res.render('contest_edit', {
contest: contest, contest: contest,
problems: problems, problems: problems,
contest_id,
admins: admins admins: admins
}); });
} catch (e) { } catch (e) {
@ -63,6 +64,17 @@ app.get('/contest/:id/edit', async (req, res) => {
} }
}); });
app.get('/api/contest/problem/:id', async (req, res) => {
try {
const id = req.params.id;
let contest = await Contest.findById(id);
let problems = await contest.problems.split('|').mapAsync(async id => await Problem.findById(id));
res.send({ problems});
} catch(e) {
res.send({ error_code: e.errno, error_msg: '失败' });
}
})
app.post('/contest/:id/edit', async (req, res) => { app.post('/contest/:id/edit', async (req, res) => {
try { try {
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。');
@ -96,7 +108,7 @@ app.post('/contest/:id/edit', async (req, res) => {
if (!req.body.title.trim()) throw new ErrorMessage('比赛名不能为空。'); if (!req.body.title.trim()) throw new ErrorMessage('比赛名不能为空。');
contest.title = req.body.title; contest.title = req.body.title;
contest.subtitle = req.body.subtitle; contest.subtitle = req.body.subtitle;
if (!Array.isArray(req.body.problems)) req.body.problems = [req.body.problems]; if (!Array.isArray(req.body.problems) && typeof req.body.problems==='string') req.body.problems = req.body.problems.split(',');
if (!Array.isArray(req.body.admins)) req.body.admins = [req.body.admins]; if (!Array.isArray(req.body.admins)) req.body.admins = [req.body.admins];
contest.problems = req.body.problems.join('|'); contest.problems = req.body.problems.join('|');
contest.admins = req.body.admins.join('|'); contest.admins = req.body.admins.join('|');

5
modules/submission.js

@ -85,6 +85,11 @@ app.get('/submissions', async (req, res) => {
isFiltered = true; isFiltered = true;
} }
if (req.query.isshare) {
query.andWhere('is_share = :status', { status: req.query.isshare });
}
if (!inContest && (!curUser || !await curUser.hasPrivilege('manage_problem'))) { if (!inContest && (!curUser || !await curUser.hasPrivilege('manage_problem'))) {
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);

2
views/admin_classify.ejs

@ -71,6 +71,7 @@
:total="problemCount"> :total="problemCount">
</el-pagination> </el-pagination>
</div> </div>
<!-- <div is='problem-table' @getuserchoose="handleUserChoose" :userselectarray="multipleSelection" :disabledidarray="disabledId"></div>-->
<button class="ui button" @click="submitInfo">保存</button> <button class="ui button" @click="submitInfo">保存</button>
</div> </div>
</div> </div>
@ -112,6 +113,7 @@
<!-- 引入组件库 --> <!-- 引入组件库 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script> <script src="https://unpkg.com/element-ui/lib/index.js"></script>
<% include problem_table %>
<script> <script>
new window.Vue({ new window.Vue({
el: '#classify', el: '#classify',

129
views/contest_edit.ejs

@ -1,8 +1,8 @@
<% this.title = contest.id ? '编辑比赛' : '新建比赛' %> <% this.title = contest.id ? '编辑比赛' : '新建比赛' %>
<% include header %> <% include header %>
<% include monaco-editor %> <% include monaco-editor %>
<div class="padding"> <div class="padding" id="edit_contest">
<form action="<%= syzoj.utils.makeUrl(['contest', contest.id, 'edit']) %>" method="post"> <form action="<%= syzoj.utils.makeUrl(['contest', contest.id, 'edit']) %>" method="post" id="form">
<div class="ui form"> <div class="ui form">
<div class="field"> <div class="field">
<label>比赛名称</label> <label>比赛名称</label>
@ -19,11 +19,37 @@
<div class="ui form"> <div class="ui form">
<div class="field"> <div class="field">
<label>试题列表</label> <label>试题列表</label>
<select class="ui fluid search dropdown" multiple="" id="search_problems" name="problems"> <!-- <% include problem_table %>-->
<% for (let problem of problems) { %> <input type="input" hidden name="problems" v-model="calcProblemsValue" />
<option value="<%= problem.id %>" selected>#<%= problem.id %>. <%= problem.title %></option> <el-tag @close="handleClose(item)" closable v-for="item in userselectarray" style="margin-right:5px">#{{item.id}}{{item.title}}</el-tag>
<% } %> <div class="field">
</select> <el-table
ref="multipleTable"
:data="datas"
tooltip-effect="dark"
style="width: 100%"
@select="handleSelectionChange"
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="题目id">
</el-table-column>
<el-table-column
prop="title"
label="题目标题">
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePageChange"
layout="prev, pager, next"
:current-page.sync="currentPage"
:total="max">
</el-pagination>
</div>
</div> </div>
<div class="field"> <div class="field">
<label>比赛管理员</label> <label>比赛管理员</label>
@ -93,6 +119,95 @@
</div> </div>
</form> </form>
<script> <script>
new window.Vue({
el:'#edit_contest',
data: {
userselectarray: [],
allproblemList:[],
datas: [],
currentPage:1,
max: 0,
arrayItem: null
},
mounted() {
this.loadProblem();
},
methods: {
loadProblem() {
fetch('/api/pagination/allproblem').then(res => res.json()).then(data => {
this.max = parseInt(data.problemInfo.length);
this.allproblemList = data.problemInfo;
this.datas = data.problemInfo.slice(0,10);
this.loadData();
})
},
loadData() {
let id = <%= contest_id %>;
fetch(`/api/contest/problem/${id}`).then(res=> res.json()).then(subitem => {
const originUserselectarray = subitem.problems.map((item) =>{
return {
id: item.id,
title: item.title
}
});
const userselectarrayId = originUserselectarray.map(item=>item.id);
const userSelectArray = this.datas.filter((item) => {
return userselectarrayId.includes(item.id);
})
this.toggleSelection(userSelectArray);
this.userselectarray = userSelectArray;
})
},
handleClose(tag) {
const deleteItem = this.userselectarray.splice(this.userselectarray.indexOf(tag), 1);
this.toggleSelection(deleteItem)
},
getProblemPagination(page) {
let that = this;
this.datas = this.allproblemList.slice((page - 1)*10, (page - 1)*10+10);
this.$nextTick(function(){
that.toggleSelection(that.userselectarray);
})
},
selectProblem: function(array) {
this.toggleSelection(array)
},
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handlePageChange(page){
this.selectProblem(this.userselectarray);
this.getProblemPagination(page);
},
handleSelectionChange(val, row){
const multipleSelectionId = this.userselectarray.map(item => item.id);
if (multipleSelectionId.includes(row.id)) {
this.userselectarray.splice(this.userselectarray.indexOf(row), 1)
} else {
this.userselectarray.push(row);
}
},
disableCheckbox: function(row) {
if (this.disabledidarray) {
return !this.disabledidarray.includes(row.id);
}
return true;
},
},
computed: {
calcProblemsValue:function(){
console.log(this.userselectarray.map(item=>item.id));
return this.userselectarray.map(item=>item.id);
}
}
})
var editors = { var editors = {
subtitle: { defaultValue: <%- serializejs(contest.subtitle) %> }, subtitle: { defaultValue: <%- serializejs(contest.subtitle) %> },
information: { defaultValue: <%- serializejs(contest.information) %> } information: { defaultValue: <%- serializejs(contest.information) %> }

4
views/header.ejs

@ -33,6 +33,10 @@
<link href="<%- this.builtInCdnUrl %>/google-fonts/exo-2.css" rel="stylesheet"> <link href="<%- this.builtInCdnUrl %>/google-fonts/exo-2.css" rel="stylesheet">
<% } %> <% } %>
<script src="<%- lib('jquery/3.3.1/jquery.min.js') %>"></script> <script src="<%- lib('jquery/3.3.1/jquery.min.js') %>"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" />
<!-- 引入组件库 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<% if (syzoj.config.google_analytics && syzoj.config.google_analytics !== 'UA-XXXXXXXX-X') { %> <% if (syzoj.config.google_analytics && syzoj.config.google_analytics !== 'UA-XXXXXXXX-X') { %>
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){

130
views/problem_table.ejs

@ -0,0 +1,130 @@
<% include util %>
<script>
Vue.component('problem-table', {
template: '#problemTable',
props: ['disabledidarray', 'userselectarray'],// 禁止选择的选项, 用户已经选择的选项
data() {
return {
allproblemList:[],
checkedArray: [],
datas: [],
currentPage:1,
max: 0,
arrayItem: null
}
},
watch: {
allproblemList: function(newData, olbData){
this.datas = newData.slice(0,10);
},
userselectarray: function(newData, olbData) {
console.log('更新了');
// this.checkedArray = newData;
this.toggleSelection(newData)
},
checkedArray: function(newdata, olddata){
console.log('checked更新了');
this.toggleSelection(newdata)
}
},
mounted(){
console.log('加载');
console.log(this.userselectarray);
this.getCount();
},
methods:{
getCount: function(){
let that = this;
$.ajax({
url: '/api/pagination/allproblem',
type: 'GET',
success: function (data) {
that.max = parseInt(data.problemInfo.length);
that.allproblemList = data.problemInfo;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('创建失败');
}
})
},
handleClose(tag) {
const deleteItem = this.checkedArray.splice(this.checkedArray.indexOf(tag), 1);
this.toggleSelection(deleteItem)
},
getProblemPagination(page) {
let that = this;
this.datas = this.allproblemList.slice((page - 1)*10, (page - 1)*10+10);
this.$nextTick(function(){
that.toggleSelection(that.checkedArray);
})
},
selectProblem: function(array) {
this.toggleSelection(array)
},
toggleSelection(rows) {
if (rows) {
console.log(rows);
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handlePageChange(page){
this.selectProblem(this.checkedArray);
this.getProblemPagination(page);
},
handleSelectionChange(val, row){
const multipleSelectionId = this.checkedArray.map(item => item.id);
if (multipleSelectionId.includes(row.id)) {
this.checkedArray.splice(this.checkedArray.indexOf(row), 1)
} else {
this.checkedArray.push(row);
}
// this.$emit('getuserchoose', this.checkedArray)
},
disableCheckbox: function(row) {
if (this.disabledidarray) {
return !this.disabledidarray.includes(row.id);
}
return true;
},
},
});
</script>
<script id="problemTable" type="text/x-template">
<div>
<div class="field">
<label>当前练习阶段已经选择的题</label>
<el-table
ref="multipleTable"
:data="datas"
tooltip-effect="dark"
style="width: 100%"
@select="handleSelectionChange"
>
<el-table-column
type="selection"
:selectable="disableCheckbox"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="题目id">
</el-table-column>
<el-table-column
prop="title"
label="题目标题">
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePageChange"
layout="prev, pager, next"
:current-page.sync="currentPage"
:total="max">
</el-pagination>
</div>
</div>
</script>

69
views/submissions.ejs

@ -1,6 +1,12 @@
<% this.title = '提交记录' %> <% this.title = '提交记录' %>
<% include header %> <% include header %>
<% include util %> <% include util %>
<style>
.label{
font-size: 1.2em;
margin-right: 1px;
}
</style>
<script src="<%- lib('textfit/2.3.1/textFit.min.js') %>"></script> <script src="<%- lib('textfit/2.3.1/textFit.min.js') %>"></script>
<div class="padding"> <div class="padding">
<% if (displayConfig.inContest) { %> <% if (displayConfig.inContest) { %>
@ -17,10 +23,10 @@
<form action="<%= syzoj.utils.makeUrl(displayConfig.inContest ? ['contest', contest.id, 'submissions'] : ['submissions']) %>" class="ui mini form" method="get" role="form" id="form"> <form action="<%= syzoj.utils.makeUrl(displayConfig.inContest ? ['contest', contest.id, 'submissions'] : ['submissions']) %>" class="ui mini form" method="get" role="form" id="form">
<input type="hidden" name="contest" value="<%= form.contest %>" /> <input type="hidden" name="contest" value="<%= form.contest %>" />
<div class="inline fields" style="margin-bottom: 25px; white-space: nowrap; "> <div class="inline fields" style="margin-bottom: 25px; white-space: nowrap; ">
<label style="font-size: 1.2em; margin-right: 1px; ">题目:</label> <label class="label">题目:</label>
<div class="field"><input name="problem_id" style="width: 50px; " type="text" value="<%= form.problem_id %>"></div> <div class="field"><input name="problem_id" style="width: 50px; " type="text" value="<%= form.problem_id %>"></div>
<% if (displayConfig.showOthers) { %> <% if (displayConfig.showOthers) { %>
<label style="font-size: 1.2em; margin-right: 1px; ">提交者:</label> <label class="label">提交者:</label>
<div class="field"><input name="submitter" style="width: 100px; " type="text" value="<%= form.submitter %>"></div> <div class="field"><input name="submitter" style="width: 100px; " type="text" value="<%= form.submitter %>"></div>
<% } %> <% } %>
<% if (displayConfig.showScore) { %> <% if (displayConfig.showScore) { %>
@ -29,7 +35,7 @@
<label style="font-size: 1.2em; margin-right: 7px; ">~</label> <label style="font-size: 1.2em; margin-right: 7px; ">~</label>
<div class="field"><input name="max_score" style="width: 45px; " type="text" value="<%= form.max_score || 100 %>"></div> <div class="field"><input name="max_score" style="width: 45px; " type="text" value="<%= form.max_score || 100 %>"></div>
<% } %> <% } %>
<label style="font-size: 1.2em; margin-right: 1px; ">语言:</label> <label class="label">语言:</label>
<div class="field"> <div class="field">
<div class="ui fluid selection dropdown" id="select_language" style="width: 110px; "> <div class="ui fluid selection dropdown" id="select_language" style="width: 110px; ">
<input type="hidden" name="language" value="<%= form.language %>"> <input type="hidden" name="language" value="<%= form.language %>">
@ -46,7 +52,7 @@
</div> </div>
</div> </div>
<% if (displayConfig.showResult) { %> <% if (displayConfig.showResult) { %>
<label style="font-size: 1.2em; margin-right: 1px; ">状态:</label> <label class="label">状态:</label>
<div class="field"> <div class="field">
<div class="ui fluid selection dropdown" id="select_status" style="width: 210px; "> <div class="ui fluid selection dropdown" id="select_status" style="width: 210px; ">
<input type="hidden" name="status" value="<%= form.status %>"> <input type="hidden" name="status" value="<%= form.status %>">
@ -62,29 +68,45 @@
</div> </div>
</div> </div>
<% } %> <% } %>
<button class="ui labeled icon mini button" type="submit"> <label class="label">代码是否分享:</label>
<i class="search icon"></i> <div class="field">
查询 <div class="ui fluid selection dropdown" id="select_share" style="width: 110px; ">
</button> <input type="hidden" name="isshare" value="<%= form.isshare %>">
<% if (user && displayConfig.showOthers) { %> <i class="dropdown icon"></i>
<a class="ui mini labeled icon blue button" style="margin-left: auto; " id="my_submit"> <div class="default text"></div>
<i class="user icon"></i> <div class="menu">
我的提交 <div class="item" data-value=""><b>不限</b></div>
</a> <div class="item" data-value="1"><b>是</b></div>
<script> <div class="item" data-value="0"><b>否</b></div>
$(function () { </div>
$('#my_submit').click(function () { </div>
$('[name=submitter]').val(<%- serializejs(user.username) %>); </div>
$('#form').submit(); </div>
}); <div class="inline fields">
}); <button class="ui labeled icon mini button" type="submit">
</script> <i class="search icon"></i>
<% } %> 查询
</button>
<% if (user && displayConfig.showOthers) { %>
<a class="ui mini labeled icon blue button" id="my_submit">
<i class="user icon"></i>
我的提交
</a>
<script>
$(function () {
$('#my_submit').click(function () {
$('[name=submitter]').val(<%- serializejs(user.username) %>);
$('#form').submit();
});
});
</script>
<% } %>
</div> </div>
</form> </form>
<table id="vueAppFuckSafari" class="ui very basic center aligned table" style="white-space: nowrap; " id="table"> <table id="vueAppFuckSafari" class="ui very basic center aligned table" style="white-space: nowrap; " id="table">
<thead> <thead>
<tr> <tr>
<th>是否已经分享</th>
<th>编号</th> <th>编号</th>
<th>题目</th> <th>题目</th>
<th>状态</th> <th>状态</th>
@ -133,11 +155,14 @@
$(function () { $(function () {
$('#select_language').dropdown(); $('#select_language').dropdown();
$('#select_status').dropdown(); $('#select_status').dropdown();
$('#select_share').dropdown();
}); });
const itemList = <%- serializejs(items) %>; const itemList = <%- serializejs(items) %>;
const socketUrl = "/rough"; const socketUrl = "/rough";
const displayConfig = <%- serializejs(displayConfig) %>; const displayConfig = <%- serializejs(displayConfig) %>;
console.log(itemList);
const vueApp = new Vue({ const vueApp = new Vue({
el: '#vueAppFuckSafari', el: '#vueAppFuckSafari',
data: { data: {

1
views/submissions_item.ejs

@ -66,6 +66,7 @@
<script id="submissionItemTemplate" type="text/x-template"> <script id="submissionItemTemplate" type="text/x-template">
<tr> <tr>
<td><i class="icon" :class="data.info.is_share ? 'smile': 'meh'" style="font-size: 1.5em"></i></td>
<% if (active === 'submissions') { %> <% if (active === 'submissions') { %>
<td><a :href="submissionLink"><b>#{{ data.info.submissionId }}</b></a></td> <td><a :href="submissionLink"><b>#{{ data.info.submissionId }}</b></a></td>
<% } else { %> <% } else { %>

Loading…
Cancel
Save