前端页面添加
This commit is contained in:
parent
b31b8a9b3c
commit
98f0c2da2f
|
@ -13,6 +13,7 @@
|
||||||
| 2020-10-30 | 评测模块接口,判题服务系统,初始化前端vue项目 | Himit_ZH |
|
| 2020-10-30 | 评测模块接口,判题服务系统,初始化前端vue项目 | Himit_ZH |
|
||||||
| 2020-11-08 | 前端vue主页,题目列表页,登录,注册,重置密码弹窗逻辑 | Himit_ZH |
|
| 2020-11-08 | 前端vue主页,题目列表页,登录,注册,重置密码弹窗逻辑 | Himit_ZH |
|
||||||
| 2020-11-16 | 前端提交列表页,提交详情页,题目详情页,排行(ACM,OI)页,比赛列表页,个人主页,个人设置页 | Himit_ZH |
|
| 2020-11-16 | 前端提交列表页,提交详情页,题目详情页,排行(ACM,OI)页,比赛列表页,个人主页,个人设置页 | Himit_ZH |
|
||||||
|
| 2020-11-22 | 前端比赛首页,比赛题目列表,比赛排行榜,比赛公告,首页布局调整 | Himit_ZH |
|
||||||
|
|
||||||
# 二、系统架构
|
# 二、系统架构
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<NavBar></NavBar>
|
<NavBar></NavBar>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<transition name="fadeInUp" mode="out-in">
|
<transition name="el-zoom-in-bottom">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,6 +24,7 @@ export default {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
}
|
}
|
||||||
body{
|
body{
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
@ -35,7 +36,7 @@ export default {
|
||||||
code, kbd, pre, samp {
|
code, kbd, pre, samp {
|
||||||
font-family: Consolas,Menlo,Courier,monospace;
|
font-family: Consolas,Menlo,Courier,monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -57,6 +58,27 @@ export default {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-title{
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
.oi-100,.first-ac{
|
||||||
|
background-color: #080;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.oi-between{
|
||||||
|
background-color: #2d8cf0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.ac{
|
||||||
|
background-color: #a9f5af;
|
||||||
|
color: #3c763d;
|
||||||
|
}
|
||||||
|
.oi-0,.wa{
|
||||||
|
color: #a94442;
|
||||||
|
background-color: #f2dede;
|
||||||
|
}
|
||||||
.status-green{
|
.status-green{
|
||||||
background-color: #19be6b!important;
|
background-color: #19be6b!important;
|
||||||
color: #fff!important;
|
color: #fff!important;
|
||||||
|
@ -83,6 +105,10 @@ export default {
|
||||||
.vxe-table{
|
.vxe-table{
|
||||||
color: #495060!important;
|
color: #495060!important;
|
||||||
font-size: 12px!important;
|
font-size: 12px!important;
|
||||||
|
font-weight: 500!important;
|
||||||
|
}
|
||||||
|
.vxe-table .vxe-body--column:not(.col--ellipsis), .vxe-table .vxe-footer--column:not(.col--ellipsis), .vxe-table .vxe-header--column:not(.col--ellipsis) {
|
||||||
|
padding: 9px 0!important;
|
||||||
}
|
}
|
||||||
#nprogress .bar {
|
#nprogress .bar {
|
||||||
background: #66B1FF !important;
|
background: #66B1FF !important;
|
||||||
|
@ -99,10 +125,22 @@ export default {
|
||||||
padding: 0 4%;
|
padding: 0 4%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
#app {
|
||||||
|
margin-top: 210px;
|
||||||
|
padding: 0 0;
|
||||||
|
}
|
||||||
|
.markdown-body img{
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media screen and (max-width: 400px) {
|
@media screen and (max-width: 400px) {
|
||||||
#app {
|
#app {
|
||||||
margin-top: 270px;
|
margin-top: 270px;
|
||||||
padding: 0 4%;
|
padding: 0 0;
|
||||||
|
}
|
||||||
|
.markdown-body img{
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#problem-content .sample pre {
|
#problem-content .sample pre {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import mMessage from '@/common/message'
|
import mMessage from '@/common/message'
|
||||||
import NProgress from 'nprogress' // nprogress插件
|
// import NProgress from 'nprogress' // nprogress插件
|
||||||
import 'nprogress/nprogress.css' // nprogress样式
|
// import 'nprogress/nprogress.css' // nprogress样式
|
||||||
|
|
||||||
// 配置NProgress进度条选项 —— 动画效果
|
// // 配置NProgress进度条选项 —— 动画效果
|
||||||
NProgress.configure({ ease: 'ease', speed: 1000,showSpinner: false})
|
// NProgress.configure({ ease: 'ease', speed: 1000,showSpinner: false})
|
||||||
|
|
||||||
// 环境的切换
|
// 环境的切换
|
||||||
if (process.env.NODE_ENV == 'development') {
|
if (process.env.NODE_ENV == 'development') {
|
||||||
|
@ -22,7 +22,7 @@ axios.interceptors.request.use(
|
||||||
|
|
||||||
config => {
|
config => {
|
||||||
|
|
||||||
NProgress.start();
|
// NProgress.start();
|
||||||
// 每次发送请求之前判断vuex中是否存在token
|
// 每次发送请求之前判断vuex中是否存在token
|
||||||
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
|
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
|
||||||
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
|
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
|
||||||
|
@ -32,7 +32,7 @@ axios.interceptors.request.use(
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
NProgress.done();
|
// NProgress.done();
|
||||||
mmMessage.error(error.response.data.mMessage);
|
mmMessage.error(error.response.data.mMessage);
|
||||||
return Promise.error(error);
|
return Promise.error(error);
|
||||||
})
|
})
|
||||||
|
@ -40,7 +40,7 @@ axios.interceptors.request.use(
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
axios.interceptors.response.use(
|
axios.interceptors.response.use(
|
||||||
response => {
|
response => {
|
||||||
NProgress.done();
|
// NProgress.done();
|
||||||
if (response.data.status === 200) {
|
if (response.data.status === 200) {
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,7 +50,7 @@ axios.interceptors.response.use(
|
||||||
},
|
},
|
||||||
// 服务器状态码不是200的情况
|
// 服务器状态码不是200的情况
|
||||||
error => {
|
error => {
|
||||||
NProgress.done();
|
// NProgress.done();
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
switch (error.response.status) {
|
switch (error.response.status) {
|
||||||
// 401: 未登录
|
// 401: 未登录
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
export const JUDGE_STATUS = {
|
export const JUDGE_STATUS = {
|
||||||
|
'-10': {
|
||||||
|
name: 'Not submitted',
|
||||||
|
short: 'NS',
|
||||||
|
color: 'gray',
|
||||||
|
type: 'info',
|
||||||
|
rgb:'#909399'
|
||||||
|
},
|
||||||
'-3': {
|
'-3': {
|
||||||
name: 'Presentation Error',
|
name: 'Presentation Error',
|
||||||
short: 'PE',
|
short: 'PE',
|
||||||
|
@ -156,7 +163,8 @@ export const CONTEST_TYPE_REVERSE = {
|
||||||
|
|
||||||
export const CONTEST_TYPE = {
|
export const CONTEST_TYPE = {
|
||||||
PUBLIC: 'Public',
|
PUBLIC: 'Public',
|
||||||
PRIVATE: 'Password Protected'
|
PRIVATE: 'Password Protected',
|
||||||
|
PROTECT: 'Submit Protected'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const USER_TYPE = {
|
export const USER_TYPE = {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
// convert utc time to localtime
|
// convert utc time to localtime
|
||||||
function utcToLocal (utcDt, format = 'YYYY-M-D HH:mm:ss') {
|
function utcToLocal (utcDt, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
return moment.utc(utcDt).local().format(format)
|
return moment.utc(utcDt).local().format(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,22 @@ function duration (startTime, endTime) {
|
||||||
return Math.abs(duration.asHours().toFixed(1)) + ' hours'
|
return Math.abs(duration.asHours().toFixed(1)) + ' hours'
|
||||||
}
|
}
|
||||||
|
|
||||||
function secondFormat (seconds) {
|
function secondFormat (time) {
|
||||||
let m = moment.duration(seconds, 'seconds')
|
let m = moment.duration(time, 'seconds')
|
||||||
return Math.floor(m.asHours()) + ':' + m.minutes() + ':' + m.seconds()
|
let seconds = m.seconds()>=10?m.seconds():'0'+m.seconds();
|
||||||
|
let hours = Math.floor(m.asHours())>=10?Math.floor(m.asHours()):'0'+Math.floor(m.asHours());
|
||||||
|
let minutes = m.minutes()>=10?m.minutes():'0'+m.minutes();
|
||||||
|
return hours + ':' + minutes + ':' + seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function durationMs (startTime, endTime) { // 计算时间段的时间戳
|
||||||
|
let start = moment(startTime)
|
||||||
|
let end = moment(endTime)
|
||||||
|
return end.diff(start, 'seconds');
|
||||||
|
}
|
||||||
export default {
|
export default {
|
||||||
utcToLocal: utcToLocal,
|
utcToLocal: utcToLocal,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
secondFormat: secondFormat
|
secondFormat: secondFormat,
|
||||||
|
durationMs:durationMs,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import storage from '@/common/storage'
|
import storage from '@/common/storage'
|
||||||
import { STORAGE_KEY } from '@/common/constants'
|
import { STORAGE_KEY } from '@/common/constants'
|
||||||
import ojAPI from '@/common/api'
|
|
||||||
|
|
||||||
function submissionMemoryFormat (memory) {
|
function submissionMemoryFormat (memory) {
|
||||||
if (memory === undefined) return '--'
|
if (memory === undefined) return '--'
|
||||||
|
@ -39,7 +38,7 @@ function breakLongWords (value, length = 16) {
|
||||||
re = new RegExp('(.{' + length + '})', 'g')
|
re = new RegExp('(.{' + length + '})', 'g')
|
||||||
} else {
|
} else {
|
||||||
// 中文字符
|
// 中文字符
|
||||||
re = new RegExp('(.{' + (length / 2 + 1) + '})', 'g')
|
re = new RegExp('(.{' + (parseInt(length / 2) + 1) + '})', 'g')
|
||||||
}
|
}
|
||||||
return value.replace(re, '$1\n')
|
return value.replace(re, '$1\n')
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
<template>
|
||||||
|
<el-card shadow :padding="10">
|
||||||
|
<div slot="header">
|
||||||
|
<span class="panel-title" v-if="isContest">{{ title }}</span>
|
||||||
|
<span v-else class="home-title panel-title">{{ title}}</span>
|
||||||
|
<span style="float: right">
|
||||||
|
<el-button
|
||||||
|
v-if="listVisible"
|
||||||
|
type="primary"
|
||||||
|
@click="init"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-refresh"
|
||||||
|
:loading="btnLoading"
|
||||||
|
>Refresh</el-button
|
||||||
|
>
|
||||||
|
<el-button v-else type="primary" icon="el-icon-back" @click="goBack" size="small"
|
||||||
|
>Back</el-button
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<transition-group name="el-zoom-in-bottom">
|
||||||
|
<div
|
||||||
|
class="no-announcement"
|
||||||
|
v-if="!announcements.length"
|
||||||
|
key="no-announcement"
|
||||||
|
>
|
||||||
|
<p>No Announcements</p>
|
||||||
|
</div>
|
||||||
|
<template v-if="listVisible">
|
||||||
|
<ul class="announcements-container" key="list">
|
||||||
|
<li v-for="announcement in announcements" :key="announcement.title">
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="title">
|
||||||
|
<a class="entry" @click="goAnnouncement(announcement)">
|
||||||
|
{{ announcement.title }}</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<span class="date">
|
||||||
|
|
||||||
|
<i class="el-icon-edit"></i>
|
||||||
|
{{ announcement.create_time | localtime }}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<span class="creator">
|
||||||
|
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
|
{{ announcement.created_by.username }}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Pagination
|
||||||
|
v-if="!isContest"
|
||||||
|
key="page"
|
||||||
|
:total="total"
|
||||||
|
:page-size="limit"
|
||||||
|
@on-change="getAnnouncementList"
|
||||||
|
>
|
||||||
|
</Pagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-katex
|
||||||
|
v-html="announcement.content"
|
||||||
|
key="content"
|
||||||
|
class="content-container markdown-body"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import api from "@/common/api";
|
||||||
|
import Pagination from "@/components/common/Pagination";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Announcement",
|
||||||
|
components: {
|
||||||
|
Pagination,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
limit: 10,
|
||||||
|
total: 10,
|
||||||
|
btnLoading: false,
|
||||||
|
announcements: [{
|
||||||
|
"id": 6,
|
||||||
|
"created_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "root",
|
||||||
|
"real_name": null
|
||||||
|
},
|
||||||
|
"title": "\u8d5e\u52a9\u672c\u7ad9\u7684\u670d\u52a1\u5668 O(\u2229_\u2229)O\u8c22\u8c22",
|
||||||
|
"content": "<p><img alt=\"IMG_0264.JPG\" src=\"/public/upload/4cd6928f19.jpg\" width=\"425\" height=\"346\" /><br /></p><p><br /></p><ul><li>\u8d5e\u52a9\u672c\u7ad9\u8d2d\u4e70\u66f4\u597d\u7684\u670d\u52a1\u5668\uff0c\u9632\u6b62\u90e8\u5206\u9898\u76ee\u548c\u7f16\u8bd1\u5668\u975e\u9884\u671f\u7684\u8d85\u65f6</li><li>\u8d5e\u52a9\u672c\u7ad9\u7684CDN</li></ul><p><span style=\"color: rgb(65, 140, 175);\">O(\u2229_\u2229)O\u8c22\u8c22</span></p>",
|
||||||
|
"create_time": "2019-06-06T11:37:14.950009Z",
|
||||||
|
"last_update_time": "2019-10-10T14:08:25.887019Z",
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"created_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "root",
|
||||||
|
"real_name": null
|
||||||
|
},
|
||||||
|
"title": "\u672c\u7ad9\u5c06\u5728\u672a\u6765\u4e00\u5468\u5185\u968f\u65f6\u8fc1\u79fb\u90e8\u7f72\u673a\u5668\uff0c\u5982\u6709\u9700\u6c42\u8bf7\u63d0\u524d\u7533\u8bf7 [5.3 \u5df2\u7ecf\u5b8c\u6210]",
|
||||||
|
"content": "<p>RT</p><p><br /></p><p>Update:5.3 \u5df2\u7ecf\u5b8c\u6210</p>",
|
||||||
|
"create_time": "2019-04-28T02:29:40.726247Z",
|
||||||
|
"last_update_time": "2019-05-03T02:23:10.504187Z",
|
||||||
|
"visible": true
|
||||||
|
},],
|
||||||
|
announcement: "",
|
||||||
|
listVisible: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
if (this.isContest) {
|
||||||
|
this.getContestAnnouncementList();
|
||||||
|
} else {
|
||||||
|
this.getAnnouncementList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAnnouncementList(page = 1) {
|
||||||
|
this.btnLoading = true;
|
||||||
|
api.getAnnouncementList((page - 1) * this.limit, this.limit).then(
|
||||||
|
(res) => {
|
||||||
|
this.btnLoading = false;
|
||||||
|
this.announcements = res.data.data.results;
|
||||||
|
this.total = res.data.data.total;
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.btnLoading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getContestAnnouncementList() {
|
||||||
|
this.btnLoading = true;
|
||||||
|
api.getContestAnnouncementList(this.$route.params.contestID).then(
|
||||||
|
(res) => {
|
||||||
|
this.btnLoading = false;
|
||||||
|
this.announcements = res.data.data;
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.btnLoading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
goAnnouncement(announcement) {
|
||||||
|
this.announcement = announcement;
|
||||||
|
this.listVisible = false;
|
||||||
|
},
|
||||||
|
goBack() {
|
||||||
|
this.listVisible = true;
|
||||||
|
this.announcement = "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.listVisible) {
|
||||||
|
return this.isContest ? "Contest Announcements" : "Announcements";
|
||||||
|
} else {
|
||||||
|
return this.announcement.title;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isContest() {
|
||||||
|
return !!this.$route.params.contestID;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.announcements-container {
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.announcements-container li {
|
||||||
|
padding-top: 15px;
|
||||||
|
list-style: none;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top:10px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: 1px solid rgba(187, 187, 187, 0.5);
|
||||||
|
border-left: 2px solid #409EFF;
|
||||||
|
}
|
||||||
|
/* .announcements-container li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
} */
|
||||||
|
.flex-container{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.flex-container .info{
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container .title .entry {
|
||||||
|
color: #495060;
|
||||||
|
font-style: oblique;
|
||||||
|
}
|
||||||
|
.flex-container .title a:hover {
|
||||||
|
color: #2d8cf0;
|
||||||
|
border-bottom: 1px solid #2d8cf0;
|
||||||
|
}
|
||||||
|
.creator {
|
||||||
|
width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.date {
|
||||||
|
width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
padding: 0 20px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-announcement {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-animate-enter-active {
|
||||||
|
animation: fadeIn 1s;
|
||||||
|
}
|
||||||
|
ul{
|
||||||
|
list-style-type:none;
|
||||||
|
padding-inline-start:0px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,13 +10,11 @@ import ContestList from "@/views/contest/ContestList.vue"
|
||||||
import Problem from "@/views/problem/Problem.vue"
|
import Problem from "@/views/problem/Problem.vue"
|
||||||
import ACMRank from "@/views/rank/ACMRank.vue"
|
import ACMRank from "@/views/rank/ACMRank.vue"
|
||||||
import OIRank from "@/views/rank/OIRank.vue"
|
import OIRank from "@/views/rank/OIRank.vue"
|
||||||
import CountDown from "@/views/contest/test.vue"
|
import ContestDetails from "@/views/contest/ContestDetails.vue"
|
||||||
|
import ContestProblemList from "@/views/contest/children/ContestProblemList.vue"
|
||||||
|
import ContestRank from "@/views/contest/children/ContestRank.vue"
|
||||||
|
import Announcements from "@/components/common/Announcements.vue"
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
|
||||||
path: '/count-down',
|
|
||||||
name: 'CountDown',
|
|
||||||
component: CountDown
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: {
|
redirect: {
|
||||||
|
@ -86,5 +84,43 @@ const routes = [
|
||||||
component: Logout,
|
component: Logout,
|
||||||
meta: { requireAuth: true, title: 'Logout' }
|
meta: { requireAuth: true, title: 'Logout' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'contest-details',
|
||||||
|
path: '/contest/:contestID/',
|
||||||
|
component:ContestDetails,
|
||||||
|
meta: {title: 'Contest Details'},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'contest-submission-list',
|
||||||
|
path: 'submissions',
|
||||||
|
component: SubmissionList
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contest-problem-list',
|
||||||
|
path: 'problems',
|
||||||
|
component: ContestProblemList
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contest-problem-details',
|
||||||
|
path: 'problem/:problemID/',
|
||||||
|
component: Problem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contest-announcement-list',
|
||||||
|
path: 'announcements',
|
||||||
|
component: Announcements
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contest-rank',
|
||||||
|
path: 'rank',
|
||||||
|
component: ContestRank
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'acm-helper',
|
||||||
|
// path: 'helper',
|
||||||
|
// component: ACMContestHelper
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
export default routes
|
export default routes
|
||||||
|
|
|
@ -138,8 +138,8 @@ const actions = {
|
||||||
api.getContest(rootState.route.params.contestID).then((res) => {
|
api.getContest(rootState.route.params.contestID).then((res) => {
|
||||||
resolve(res)
|
resolve(res)
|
||||||
let contest = res.data.data
|
let contest = res.data.data
|
||||||
commit(types.CHANGE_CONTEST, {contest: contest})
|
commit('changeContest', {contest: contest})
|
||||||
commit(types.NOW, {now: moment(contest.now)})
|
commit('now', {now: moment(contest.now)})
|
||||||
if (contest.contest_type === CONTEST_TYPE.PRIVATE) {
|
if (contest.contest_type === CONTEST_TYPE.PRIVATE) {
|
||||||
dispatch('getContestAccess')
|
dispatch('getContestAccess')
|
||||||
}
|
}
|
||||||
|
@ -159,17 +159,17 @@ const actions = {
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
})
|
})
|
||||||
commit(types.CHANGE_CONTEST_PROBLEMS, {contestProblems: res.data.data})
|
commit('changeContestProblems', {contestProblems: res.data.data})
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}, () => {
|
}, () => {
|
||||||
commit(types.CHANGE_CONTEST_PROBLEMS, {contestProblems: []})
|
commit('changeContestProblems', {contestProblems: []})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getContestAccess ({commit, rootState}) {
|
getContestAccess ({commit, rootState}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
api.getContestAccess(rootState.route.params.contestID).then(res => {
|
api.getContestAccess(rootState.route.params.contestID).then(res => {
|
||||||
commit(types.CONTEST_ACCESS, {access: res.data.data.access})
|
commit('contestAccess', {access: res.data.data.access})
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch()
|
}).catch()
|
||||||
})
|
})
|
||||||
|
|
|
@ -76,9 +76,9 @@ const rootActions = {
|
||||||
},
|
},
|
||||||
changeDomTitle ({commit, state}, payload) {
|
changeDomTitle ({commit, state}, payload) {
|
||||||
if (payload && payload.title) {
|
if (payload && payload.title) {
|
||||||
window.document.title = state.website.website_name_shortcut + ' | ' + payload.title
|
window.document.title = 'HOJ' + ' - ' + payload.title
|
||||||
} else {
|
} else {
|
||||||
window.document.title = state.website.website_name_shortcut + ' | ' + state.route.meta.title
|
window.document.title = 'HOJ' + ' - ' + state.route.meta.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,126 +1,111 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-card class="contest">
|
<el-card class="contest">
|
||||||
<div slot="header" class="clearfix title">
|
<div slot="header" class="clearfix title">
|
||||||
<el-link @click="goContest" :underline="false">比赛标题</el-link>
|
<el-link @click="goContest" :underline="false">比赛标题</el-link>
|
||||||
</div>
|
</div>
|
||||||
<el-carousel indicator-position="outside" height="200px" :interval='interval'>
|
<el-carousel
|
||||||
<el-carousel-item v-for="(contest,index) in contests" :key="index">
|
indicator-position="outside"
|
||||||
<div class="contest-info">
|
height="200px"
|
||||||
|
:interval="interval"
|
||||||
|
>
|
||||||
|
<el-carousel-item v-for="(contest, index) in contests" :key="index">
|
||||||
|
<div class="contest-info">
|
||||||
<div class="contest-tags">
|
<div class="contest-tags">
|
||||||
<el-button type="primary" round size="mini"><i class="fa fa-calendar"></i>
|
<el-button type="primary" round size="mini"
|
||||||
{{contest.beginTime | localtime }}
|
><i class="fa fa-calendar"></i>
|
||||||
|
{{ contest.beginTime | localtime }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="success" round size="mini"><i class="fa fa-clock-o"></i>
|
<el-button type="success" round size="mini"
|
||||||
{{contest.duration}}
|
><i class="fa fa-clock-o"></i>
|
||||||
|
{{ contest.duration }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="warning" round size="mini"><i class="fa fa-trophy"></i>
|
<el-button type="warning" round size="mini"
|
||||||
{{contest.type}}
|
><i class="fa fa-trophy"></i>
|
||||||
|
{{ contest.type }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="contest-description">
|
<div class="contest-description">
|
||||||
<blockquote v-html="contest.description"></blockquote>
|
<blockquote v-html="contest.description"></blockquote>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-carousel-item>
|
</el-carousel-item>
|
||||||
</el-carousel>
|
</el-carousel>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-row>
|
<el-row :gutter="20">
|
||||||
<el-col :md="14" :sm="24">
|
<el-col :md="12" :sm="24">
|
||||||
<el-card class="box-card">
|
<Announcements></Announcements>
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<h2>Other OnlineJudge Contest</h2>
|
|
||||||
</div>
|
|
||||||
<el-table
|
|
||||||
:data="tableData"
|
|
||||||
height="400"
|
|
||||||
border
|
|
||||||
style="width: 100%;border-radius: 8px;">
|
|
||||||
<el-table-column
|
|
||||||
prop="oj"
|
|
||||||
label="OJ"
|
|
||||||
width="100">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="title"
|
|
||||||
label="Title"
|
|
||||||
>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="beginTime"
|
|
||||||
width="160"
|
|
||||||
label="Begin time">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="endTime"
|
|
||||||
width="160"
|
|
||||||
label="End time">
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
<el-col :md="10" :sm="24">
|
|
||||||
<div class="block">
|
|
||||||
<el-timeline>
|
|
||||||
<el-timeline-item timestamp="2018/4/12" placement="top" icon="el-icon-more" type="primary">
|
|
||||||
<el-card>
|
|
||||||
<h4>更新 Github 模板</h4>
|
|
||||||
<p>王小虎 提交于 2018/4/12 20:46</p>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2018/4/3" placement="top">
|
|
||||||
<el-card>
|
|
||||||
<h4>更新 Github 模板</h4>
|
|
||||||
<p>王小虎 提交于 2018/4/3 20:46</p>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2018/4/2" placement="top">
|
|
||||||
<el-card>
|
|
||||||
<h4>更新 Github 模板</h4>
|
|
||||||
<p>王小虎 提交于 2018/4/2 20:46</p>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
</el-timeline>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
<el-col :md="12" :sm="24">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span class="panel-title home-title"
|
||||||
|
>Other OnlineJudge Contest</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<vxe-table
|
||||||
|
border="inner"
|
||||||
|
stripe
|
||||||
|
auto-resize
|
||||||
|
:data="otherContests">
|
||||||
|
<vxe-table-column field="oj" title="OJ" min-width="100"></vxe-table-column>
|
||||||
|
<vxe-table-column field="title" title="Title" min-width="200"></vxe-table-column>
|
||||||
|
<vxe-table-column field="beginTime" title="Begin Time" min-width="150">
|
||||||
|
<template v-slot="{ row }" >
|
||||||
|
<span>{{row.beginTime| localtime}}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="endTime" title="End Time" min-width="150">
|
||||||
|
<template v-slot="{ row }" >
|
||||||
|
<span>{{row.endTime| localtime}}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import time from "@/common/time";
|
import time from "@/common/time";
|
||||||
|
import Announcements from "@/components/common/Announcements.vue";
|
||||||
export default {
|
export default {
|
||||||
name:"home",
|
name: "home",
|
||||||
data() {
|
components: {
|
||||||
return {
|
Announcements,
|
||||||
interval:6000,
|
},
|
||||||
tableData: [{
|
data() {
|
||||||
oj: 'Codeforces',
|
return {
|
||||||
title: 'Codeforces Round #680 (Div. 1, based on VK Cup 2020-2021 - Final)',
|
interval: 6000,
|
||||||
beginTime: '2020-11-08T05:00:00Z',
|
otherContests: [
|
||||||
endTime:'2020-11-08T08:00:00Z',
|
{
|
||||||
},],
|
oj: "Codeforces",
|
||||||
contests: [
|
title:
|
||||||
{
|
"Codeforces Round #680 (Div. 1, based on VK Cup 2020-2021 - Final)",
|
||||||
beginTime:'2020-11-08T05:00:00Z',
|
beginTime: "2020-11-08T05:00:00Z",
|
||||||
duration:'5hours',
|
endTime: "2020-11-08T08:00:00Z",
|
||||||
type:"ACM",
|
},
|
||||||
description:'<h1>描述</h1>',
|
],
|
||||||
}
|
contests: [
|
||||||
],
|
{
|
||||||
}
|
beginTime: "2020-11-08T05:00:00Z",
|
||||||
},
|
duration: "5hours",
|
||||||
methods:{
|
type: "ACM",
|
||||||
goContest(){
|
description: "<h1>描述</h1>",
|
||||||
|
},
|
||||||
}
|
],
|
||||||
},
|
};
|
||||||
filters: {
|
},
|
||||||
|
methods: {
|
||||||
|
goContest() {},
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
localtime(value) {
|
localtime(value) {
|
||||||
return time.utcToLocal(value);
|
return time.utcToLocal(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.el-carousel__item h3 {
|
.el-carousel__item h3 {
|
||||||
|
@ -134,37 +119,37 @@ export default {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-col{
|
.el-col {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
.contest{
|
.contest {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.clearfix:before,
|
.clearfix:before,
|
||||||
.clearfix:after {
|
.clearfix:after {
|
||||||
display: table;
|
display: table;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
.clearfix:after {
|
.clearfix:after {
|
||||||
clear: both
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contest .title .el-link {
|
.contest .title .el-link {
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #444;
|
color: #444;
|
||||||
}
|
}
|
||||||
.clearfix h2{
|
.clearfix h2 {
|
||||||
color: #409EFF;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
.el-link.el-link--default:hover {
|
.el-link.el-link--default:hover {
|
||||||
color: #409EFF;
|
color: #409eff;
|
||||||
transition: all 0.28s ease;
|
transition: all 0.28s ease;
|
||||||
}
|
}
|
||||||
.contest .content-info {
|
.contest .content-info {
|
||||||
padding: 0 70px 40px 70px;
|
padding: 0 70px 40px 70px;
|
||||||
}
|
}
|
||||||
.contest .contest-description{
|
.contest .contest-description {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
<template>
|
||||||
|
<div class="contest-body">
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :md="24" :lg="24">
|
||||||
|
<el-card shadow>
|
||||||
|
<div class="contest-title">
|
||||||
|
<div slot="header">
|
||||||
|
<span class="panel-title">{{title}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12" class="text-align:left">
|
||||||
|
<el-tooltip :content="CONTEST_TYPE_REVERSE[auth].tips" placement="top">
|
||||||
|
<el-tag
|
||||||
|
:type="CONTEST_TYPE_REVERSE[auth].color"
|
||||||
|
effect="plain">
|
||||||
|
{{CONTEST_TYPE_REVERSE[auth].name}}
|
||||||
|
</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" style="text-align:right">
|
||||||
|
<el-button size="small">
|
||||||
|
{{ rule_type }}
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="contest-time">
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :md="12" class="left">
|
||||||
|
<p><i class="fa fa-hourglass-start" aria-hidden="true"></i> StartAt:{{startTime | localtime}}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="12" class="right">
|
||||||
|
<p><i class="fa fa-hourglass-end" aria-hidden="true"></i> EndAt:{{endTime | localtime}}</p>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="slider">
|
||||||
|
<el-slider
|
||||||
|
v-model="value"
|
||||||
|
:format-tooltip="formatTooltip"
|
||||||
|
:step="timeStep"
|
||||||
|
></el-slider>
|
||||||
|
</div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24" style="text-align:center">
|
||||||
|
<el-tag
|
||||||
|
effect="dark"
|
||||||
|
size="medium"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-circle"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
-11:16:52
|
||||||
|
<!-- {{ CONTEST_STATUS_REVERSE[status].name }} -->
|
||||||
|
</el-tag>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="sub-menu" >
|
||||||
|
<!-- 判断是否需要密码验证 -->
|
||||||
|
<el-card v-if="passwordFormVisible" class="password-form-card" style="text-align:center">
|
||||||
|
<div slot="header">
|
||||||
|
<span class="panel-title">Password required</span>
|
||||||
|
</div>
|
||||||
|
<p class="password-form-tips">To enter the Private contest,please input the password!</p>
|
||||||
|
<el-input v-model="contestPassword" type="password"
|
||||||
|
placeholder="Enter the contest password"
|
||||||
|
@on-enter="checkPassword"/>
|
||||||
|
<el-button type="primary" @click="checkPassword" style="float:right;margin:5px">Enter</el-button>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
|
||||||
|
<el-tabs v-else @tab-click="tabClick" v-model="route_name" :lazy="true">
|
||||||
|
<el-tab-pane name="contest-details">
|
||||||
|
<span slot="label"><i class="el-icon-s-home"></i> Overview</span>
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div v-html="description" class="markdown-body"></div>
|
||||||
|
</el-card>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="contest-problem-list">
|
||||||
|
<span slot="label"><i class="fa fa-list" aria-hidden="true"></i> Problems</span>
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<router-view ></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="contest-submission-list">
|
||||||
|
<span slot="label"><i class="el-icon-menu"></i> Status</span>
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<router-view></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="contest-rank">
|
||||||
|
<span slot="label"><i class="fa fa-bar-chart" aria-hidden="true"></i> Rank</span>
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<router-view></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="contest-announcement-list">
|
||||||
|
<span slot="label"><i class="fa fa-bullhorn" aria-hidden="true"></i> Announcements</span>
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<router-view></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane>
|
||||||
|
<span slot="label"><i class="fa fa-commenting" aria-hidden="true"></i> Comments</span>
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<router-view></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import time from "@/common/time";
|
||||||
|
import moment from "moment";
|
||||||
|
import api from "@/common/api";
|
||||||
|
import { mapState, mapGetters, mapActions } from "vuex";
|
||||||
|
import {
|
||||||
|
CONTEST_STATUS_REVERSE,
|
||||||
|
CONTEST_STATUS,
|
||||||
|
CONTEST_TYPE_REVERSE,
|
||||||
|
} from "@/common/constants";
|
||||||
|
export default {
|
||||||
|
name: "",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
route_name: "contest-details",
|
||||||
|
value: 0,
|
||||||
|
duration: 0, // 比赛时长时间戳
|
||||||
|
nowDurationMs: 0, // 比赛开始到现在的时间戳
|
||||||
|
timer: null,
|
||||||
|
CONTEST_STATUS: CONTEST_STATUS,
|
||||||
|
CONTEST_STATUS_REVERSE: CONTEST_STATUS_REVERSE,
|
||||||
|
CONTEST_TYPE_REVERSE: CONTEST_TYPE_REVERSE,
|
||||||
|
btnLoading: false,
|
||||||
|
contestID: "",
|
||||||
|
contestPassword: "",
|
||||||
|
status: 0,
|
||||||
|
auth: "1",
|
||||||
|
contest_type: "Public",
|
||||||
|
title:"2020\u5e74 \u67e0\u6aac\u8336\u6b22\u4e50\u8d5b \u66a8 \u672c\u79d1\u751f\u521b\u65b0\u5b9e\u9a8c\u5ba4\u6708\u8d5b",
|
||||||
|
description: "<p>\u7ef4\u4ed6\u5165\u6211\u5fc3\uff0c\u8d5b\u8fc7\u6d77\u6d1b\u56e0\uff0c\u5728\u8fd9\u4e2a\u5bd2\u51b7\u7684\u51ac\u5929\uff0c\u4e3a\u4ec0\u4e48\u4e0d\u6765\u4e00\u676f\u51b0\u723d\u67e0\u6aac\u8336\u5462\uff1f</p><p><img src=\"https://pic1.zhimg.com/d8db4bb6d8dfc727b6f0a7f12d7367a0_r.jpg?source=1940ef5c\" alt=\"\" /></p><h1>\u6bd4\u8d5b\u5956\u54c1\u8bf4\u660e:</h1><p>\u672c\u6b21\u6bd4\u8d5b\u5177\u6709\u4e30\u539a\u7684\u5956\u54c1 (\u91cd\u8981\u7684\u4e0d\u662f\u4ef7\u503c\uff0c\u800c\u662f<b>\u6b22\u4e50</b>\u4e0d\u662f\u4e48\uff1f) \uff0c\u5177\u4f53\u89c4\u5219\u5982\u4e0b\uff1a</p><p>\u4e00\u3001\u6240\u6709\u53c2\u8d5b\u9009\u624b\u5747\u83b7\u5f97<b>\u53c2\u8d5b\u7eaa\u5ff5\u5956</b>\u2014\u2014<b>\u9ea6\u65af\u5a01\u5c14\u5496\u5561\u4e00\u5305</b></p><p>\u4e8c\u3001\u672c\u6b21\u6bd4\u8d5b\u6392\u540d<b>\u7b2c\u4e00\u3001\u7b2c\u5341</b>\u7684\u9009\u624b\u5404\u83b7\u5f97LZH\u5b66\u957f\u8d5e\u52a9\u7684<b>\u7ef4\u4ed6\u67e0\u6aac\u8336\u4e09\u74f6</b></p><p><span style=\"color: rgb(51, 51, 51);\">\u4e09\u3001\u672c\u6b21\u6bd4\u8d5b\u6392\u540d<b>\u7b2c\u4e8c\u3001\u7b2c\u5341\u4e00</b>\u7684\u9009\u624b\u5404\u83b7\u5f97LZH\u5b66\u957f\u8d5e\u52a9\u7684<b>\u798f\u5efa\u9ad8\u7aef\u8336\u53f6\u4e09\u888b</b></span><br /></p><p>\u56db\u3001\u672c\u6b21\u6bd4\u8d5b<b>\u6700\u540e\u4e00\u4e2aAC</b>\u7684\u540c\u5b66\u5c06\u83b7\u5f97\u987d\u5f3a\u62fc\u640f\u5956\uff0c\u5956\u54c1\u795e\u79d8\u7684\u5f88\uff08ZXF\u8d5e\u52a9\uff09\uff0c<b>\u73b0\u573a\u63ed\u6653</b>\uff0c\u594b\u6597\u5427baby\u4eec.</p><p>\u4e94\u3001\u672c\u6b21\u6bd4\u8d5b\u83b7\u5f97<b>\u7b2c\u4e00\u540d</b>\u7684\u9009\u624b\u5c06\u83b7\u5f97ZYB\u5b66\u957f\u8d5e\u52a9\u7684<b>\u9ad8\u7aef\u8f6f\u76ae\u672c\u4e00\u4e2a</b></p><p><span style=\"color: rgb(51, 51, 51);\">\u516d\u3001\u672c\u6b21\u6bd4\u8d5b\u83b7\u5f97<b>\u7b2c\u4e8c\u3001\u4e09\u3001\u56db\u3001\u4e94\u540d</b>\u7684\u9009\u624b\u5c06\u6bcf\u4eba\u83b7\u5f97LZH\u5b66\u957f\u8d5e\u52a9\u7684<b>\u8f6f\u76ae\u672c\u4e00\u4e2a</b></span><br /></p><p><span style=\"color: rgb(51, 51, 51);\"><span style=\"color: rgb(51, 51, 51);\">\u4e03\u3001\u672c\u6b21\u6bd4\u8d5b\u83b7\u5f97</span><span style=\"color: rgb(51, 51, 51);\"><b>\u7b2c\u516d\u3001\u4e03\u3001\u516b\u3001\u4e5d\u540d</b></span><span style=\"color: rgb(51, 51, 51);\">\u7684\u9009\u624b\u5c06\u6bcf\u4eba\u83b7\u5f97CGX\u8d5e\u52a9\u7684<b>\u53cc\u6c47\u8089\u5757\u738b\u7279\u7ea7\u706b\u817f\u80a0</b></span><span style=\"color: rgb(51, 51, 51);\"><b>\u4e00\u6839</b></span><br /></span></p><p>\u516b\u3001\u6bd4\u8d5b\u7ed3\u675f\u540e\uff0c<b>\u968f\u673a\u62bd\u53d6\u4e09\u540d\u540c\u5b66</b>\u9001<b>\u725b\u5ba2\u886c\u886b</b>\uff0c\u6ee1\u8db3\u4f60\u6c38\u8fdc\u62bd\u4e0d\u5230\u886c\u886b\u7684\u975e\u914b\u7684\u613f\u671b\uff01</p><p><b><u>\u4ee5\u4e0a\u89c4\u5219\u6700\u7ec8\u89e3\u91ca\u6743\u5f52\u521b\u65b0\u5b9e\u9a8c\u5ba4\u6240\u6709</u></b>\u3002</p><h1><font>\u7f5a\u65f6\u89c4\u5219\u548c\u8fd4\u56de\u7ed3\u679c\u89e3\u91ca:</font></h1><p><font>1.\u63d0\u4ea4\u663e\u793aAccepted\u5373\u4e3a\u901a\u8fc7,\u82e5\u663e\u793a\u5176\u4ed6\u7ed3\u679c\u5373\u8868\u793a\u9519\u8bef.\u9519\u8bef\u4e00\u6b21\u603b\u7f5a\u65f6+20\u5206\u949f,\u8bf7\u8c28\u614e\u63d0\u4ea4\uff01</font></p><p><font>2.\u5173\u4e8e\u8fd4\u56de\u7ed3\u679c\u7684\u89e3\u91ca:</font></p><p><font>Pending & Juding : You solution will be judged soon, please wait for result</font></p><p><font>Compile Error : Failed to compile your source code. Click on the link to see compiler's output.</font></p><p><font>Accepted : Congratulations. Your solution is correct.</font></p><p><font>Wrong Answer : Your program's output doesn't match judger's answer.</font></p><p><font>Runtime Error : Your program terminated abnormally. Possible reasons are: segment fault, divided by zero or exited with code other than 0.OrThe memory your program actually used has exceeded limit.</font></p><p><font>Time Limit Exceeded : The CPU time your program used has exceeded limit. Java has a triple time limit.</font></p><p><font>Memory Limit Exceeded : The memory your program actually used has exceeded limit.</font></p><p><font>System Error : Oops, something has gone wrong with the judger. Please report this to administrator.</font></p><h1><font>\u6bd4\u8d5b\u8981\u6c42:</font></h1><p><font>\u6bd4\u8d5b\u8fc7\u7a0b\u4e2d<b>\u4e0d\u8bb8\u8ba8\u8bba</b>\u4e0d\u8bb8\u4e0a\u5916\u7f51,\u6709\u95ee\u9898\u53ef\u4ee5\u4e3e\u624b!</font></p><p><font><b>\u9898\u76ee\u96be\u5ea6\u4e0e\u9898\u76ee\u987a\u5e8f\u65e0\u5173,\u8bf7\u5408\u7406\u5b89\u6392\u505a\u9898\u65f6\u95f4!</b></font></p>",
|
||||||
|
real_time_rank: true,
|
||||||
|
rule_type: "ACM",
|
||||||
|
startTime: "2018-04-17T11:16:52Z",
|
||||||
|
endTime: "2018-04-22T11:16:36Z",
|
||||||
|
now: "2020-11-17T15:16:22.013824Z",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.contestID = this.$route.params.contestID;
|
||||||
|
this.route_name = this.$route.name;
|
||||||
|
if(this.route_name =='contest-problem-details'){ //特殊判断
|
||||||
|
this.route_name = 'contest-problem-list'
|
||||||
|
}
|
||||||
|
// this.$store.dispatch("getContest").then((res) => {
|
||||||
|
// this.changeDomTitle({ title: res.data.data.title });
|
||||||
|
// let data = res.data.data;
|
||||||
|
// let endTime = moment(data.end_time);
|
||||||
|
// if (endTime.isAfter(moment(data.now))) {
|
||||||
|
// this.timer = setInterval(() => {
|
||||||
|
// this.$store.commit("nowAdd1s");
|
||||||
|
// }, 1000);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
let nowTime = new Date().getTime(); // 获取当前时间戳
|
||||||
|
this.duration = time.durationMs(this.startTime, this.endTime);
|
||||||
|
this.nowDurationMs = time.durationMs(this.startTime, nowTime);
|
||||||
|
if (this.nowDurationMs >= this.duration) {
|
||||||
|
// 比赛已经结束
|
||||||
|
this.nowDurationMs = this.duration;
|
||||||
|
this.value = 100;
|
||||||
|
} else {
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
// let nowTime = new Date().getTime(); // 获取当前时间戳
|
||||||
|
// this.nowDurationMs = time.durationMs(this.startTime, nowTime); // 获取当前时间距离比赛开始的时间戳
|
||||||
|
if (this.nowDurationMs >= 0) {
|
||||||
|
// 比赛开始进度条才开始计时
|
||||||
|
this.value = (this.nowDurationMs / this.duration) * 100; // 赋予进度条新值
|
||||||
|
}
|
||||||
|
if (this.nowDurationMs >= this.duration) {
|
||||||
|
this.nowDurationMs = this.duration;
|
||||||
|
clearInterval(this.timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.nowDurationMs += 1;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(["changeDomTitle"]),
|
||||||
|
formatTooltip(val) {
|
||||||
|
if (this.nowDurationMs <= 0) {
|
||||||
|
// 还未开始
|
||||||
|
return "00:00:00";
|
||||||
|
}
|
||||||
|
return time.secondFormat(this.nowDurationMs); // 格式化时间
|
||||||
|
},
|
||||||
|
checkPassword() {
|
||||||
|
if (this.contestPassword === "") {
|
||||||
|
this.$error("Password can't be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.btnLoading = true;
|
||||||
|
api.checkContestPassword(this.contestID, this.contestPassword).then(
|
||||||
|
(res) => {
|
||||||
|
this.$success("Succeeded");
|
||||||
|
this.$store.commit(types.CONTEST_ACCESS, { access: true });
|
||||||
|
this.btnLoading = false;
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
this.btnLoading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
tabClick(tab){
|
||||||
|
let name = tab.name;
|
||||||
|
if(name !==this.$route.name){
|
||||||
|
this.$router.push({name: name});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
timeStep() {
|
||||||
|
// 时间段平分滑条长度
|
||||||
|
return 100 / this.duration;
|
||||||
|
},
|
||||||
|
...mapState({
|
||||||
|
showMenu: (state) => state.contest.itemVisible.menu,
|
||||||
|
contest: (state) => state.contest.contest,
|
||||||
|
contest_table: (state) => [state.contest.contest],
|
||||||
|
// now: (state) => state.contest.now,
|
||||||
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
"contestMenuDisabled",
|
||||||
|
"contestRuleType",
|
||||||
|
"contestStatus",
|
||||||
|
"countdown",
|
||||||
|
"isContestAdmin",
|
||||||
|
"OIContestRealTimePermission",
|
||||||
|
"passwordFormVisible",
|
||||||
|
]),
|
||||||
|
countdownColor() {
|
||||||
|
if (this.contestStatus) {
|
||||||
|
return CONTEST_STATUS_REVERSE[this.contestStatus].color;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showAdminHelper() {
|
||||||
|
return this.isContestAdmin && this.contestRuleType === "ACM";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route(newVal) {
|
||||||
|
this.route_name = newVal.name;
|
||||||
|
this.contestID = newVal.params.contestID;
|
||||||
|
this.changeDomTitle({ title: this.contest.title });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
localtime(value) {
|
||||||
|
return time.utcToLocal(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.$store.commit("clearContest");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.contest-body {
|
||||||
|
padding: 0 8%;
|
||||||
|
}
|
||||||
|
.contest-time .left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.contest-time .right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.password-form-card{
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.contest-time .left,
|
||||||
|
.contest-time .right {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/deep/.el-slider__button {
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
background-color: #409eff !important;
|
||||||
|
}
|
||||||
|
/deep/.el-slider__button-wrapper {
|
||||||
|
z-index: 500;
|
||||||
|
}
|
||||||
|
/deep/.el-slider__bar {
|
||||||
|
height: 10px !important;
|
||||||
|
background-color: #09be24 !important;
|
||||||
|
}
|
||||||
|
/deep/ .el-card__header {
|
||||||
|
border-bottom: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
/deep/.el-tabs__nav-wrap{
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
/deep/.el-tabs--top .el-tabs__item.is-top:nth-child(2){
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.contest-title{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.contest-time {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.el-tag--dark {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
.el-tag {
|
||||||
|
color: rgb(25, 190, 107);
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e9eaec;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.sub-menu {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.password-form-tips{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,624 @@
|
||||||
|
<template>
|
||||||
|
<el-card shadow>
|
||||||
|
<div slot="header"><span class="panel-title">Contest Rank</span>
|
||||||
|
<span style="float:right;font-size: 20px;">
|
||||||
|
<el-popover trigger="hover" placement="left-start">
|
||||||
|
<i class="el-icon-s-tools" slot="reference"></i>
|
||||||
|
<div id="switches">
|
||||||
|
<p>
|
||||||
|
<span>Chart</span>
|
||||||
|
<el-switch v-model="showChart"></el-switch>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Table</span>
|
||||||
|
<el-switch v-model="showTable"></el-switch>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Auto Refresh(10s)</span>
|
||||||
|
<el-switch :disabled="refreshDisabled" @on-change="handleAutoRefresh"></el-switch>
|
||||||
|
</p>
|
||||||
|
<template v-if="isContestAdmin">
|
||||||
|
<p>
|
||||||
|
<span>Force Update</span>
|
||||||
|
<el-switch :disabled="refreshDisabled" v-model="forceUpdate"></el-switch>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template>
|
||||||
|
<el-button type="primary" size="small" @click="downloadRankCSV">Download as CSV</el-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-show="showChart" class="echarts">
|
||||||
|
<ECharts :options="options" ref="chart" :autoresize="true" ></ECharts>
|
||||||
|
</div>
|
||||||
|
<div v-show="showTable">
|
||||||
|
<vxe-table
|
||||||
|
round
|
||||||
|
border
|
||||||
|
auto-resize
|
||||||
|
size="mini"
|
||||||
|
align="center"
|
||||||
|
:data="dataRank"
|
||||||
|
:cell-class-name="cellClassName"
|
||||||
|
:seq-config="{startIndex: (this.page - 1) * this.limit}"
|
||||||
|
>
|
||||||
|
<vxe-table-column field="id" type="seq" min-width="50" fixed="left"></vxe-table-column>
|
||||||
|
<vxe-table-column field="username" min-width="150" title="User" >
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span><a @click="getUserHomeByUsername(row.user.username)" style="color:rgb(87, 163, 243);">{{row.user.username}}</a>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="rating" title="AC / Total" min-width="80">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span>{{row.ac}} / <a @click="getUserTotalSubmit(row.user.username)" style="color:rgb(87, 163, 243);">{{row.total}}</a>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="total_time" title="TotalTime" min-width="80">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span>{{parseTotalTime(row.total_time)}}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column min-width="80" v-for="problem in problems" :key="problem.id">
|
||||||
|
<template v-slot:header>
|
||||||
|
<span><a @click="getContestProblemById(problem.id)">{{problem.id}}</a></span>
|
||||||
|
</template>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span v-if="row.submission_info[problem.id].is_ac">{{ row.submission_info[problem.id].ac_time }}<br></span>
|
||||||
|
<span v-if="row.submission_info[problem.id].error_number!=0">(-{{row.submission_info[problem.id].error_number}})</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
</div>
|
||||||
|
<Pagination :total="total"
|
||||||
|
:page-size.sync="limit"
|
||||||
|
:current.sync="page"
|
||||||
|
@on-change="getContestRankData"
|
||||||
|
@on-page-size-change="getContestRankData(1)"
|
||||||
|
show-sizer></Pagination>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import moment from 'moment'
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
|
||||||
|
import Pagination from '@/components/common/Pagination'
|
||||||
|
import time from '@/common/time'
|
||||||
|
import utils from '@/common/utils'
|
||||||
|
import ContestRankMixin from './contestRankMixin'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'acm-contest-rank',
|
||||||
|
mixins: [ContestRankMixin],
|
||||||
|
components: {
|
||||||
|
Pagination
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
refreshDisabled:true,
|
||||||
|
isContestAdmin:true,
|
||||||
|
showChart:true,
|
||||||
|
showTable:true,
|
||||||
|
contest:{
|
||||||
|
start_time: "2018-04-17T11:16:52Z",
|
||||||
|
end_time: "2018-04-22T11:16:36Z",
|
||||||
|
now: "2020-11-17T15:16:22.013824Z",
|
||||||
|
},
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
limit:10,
|
||||||
|
contestID: '',
|
||||||
|
dataRank: [],
|
||||||
|
problems:[],
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
text: 'Top 10 Teams',
|
||||||
|
left: 'center',
|
||||||
|
top:0
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
filterMode: 'none',
|
||||||
|
xAxisIndex: [0],
|
||||||
|
start: 0,
|
||||||
|
end: 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
show: true,
|
||||||
|
feature: {
|
||||||
|
saveAsImage: {show: true, title: 'save as image'}
|
||||||
|
},
|
||||||
|
right: '0'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
axis: 'x'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'horizontal',
|
||||||
|
x:'center',
|
||||||
|
top:'8%',
|
||||||
|
right: 0,
|
||||||
|
data: [],
|
||||||
|
formatter: (value) => {
|
||||||
|
return utils.breakLongWords(value, 16)
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: 80,
|
||||||
|
x2: 100,
|
||||||
|
left: '5%', //设置canvas图距左的距离
|
||||||
|
top: '25%',
|
||||||
|
right: '5%',
|
||||||
|
bottom: '10%'
|
||||||
|
},
|
||||||
|
xAxis: [{
|
||||||
|
type: 'time',
|
||||||
|
splitLine: false,
|
||||||
|
axisPointer: {
|
||||||
|
show: true,
|
||||||
|
snap: true
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [0]
|
||||||
|
}],
|
||||||
|
series: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.contestID = this.$route.params.contestID
|
||||||
|
let dataRank=[
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'Himit_ZH',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1000,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3600,error_number:0,is_first_ac:true,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:4500,error_number:0,is_first_ac:true,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user1',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:5555,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:6666,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'E':{ac_time:7000,error_number:0,is_first_ac:true,is_ac:true},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user2',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user2',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user3',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user4',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user5',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user6',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user7',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user6',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user7',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'我是你爸爸',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user9',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
ac:9,
|
||||||
|
total:9,
|
||||||
|
total_time:10000,
|
||||||
|
submission_info:{
|
||||||
|
'A':{ac_time:1500,error_number:3,is_first_ac:false,is_ac:true},
|
||||||
|
'B':{ac_time:3800,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'C':{ac_time:0,error_number:1,is_first_ac:false,is_ac:false},
|
||||||
|
'D':{ac_time:8000,error_number:0,is_first_ac:false,is_ac:true},
|
||||||
|
'E':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'F':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'G':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'H':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'I':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
'J':{ac_time:0,error_number:0,is_first_ac:false,is_ac:false},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
this.applyToTable(dataRank);
|
||||||
|
this.applyToChart(dataRank);
|
||||||
|
this.problems =[
|
||||||
|
{id:'A'},
|
||||||
|
{id:'B'},
|
||||||
|
{id:'C'},
|
||||||
|
{id:'D'},
|
||||||
|
{id:'E'},
|
||||||
|
{id:'F'},
|
||||||
|
{id:'G'},
|
||||||
|
{id:'H'},
|
||||||
|
{id:'I'},
|
||||||
|
{id:'J'}
|
||||||
|
];
|
||||||
|
this.addChartCategory(this.problems)
|
||||||
|
// this.getContestRankData(1)
|
||||||
|
// if (this.contestProblems.length === 0) {
|
||||||
|
// this.getContestProblems().then((res) => {
|
||||||
|
// this.addChartCategory(res.data.data)
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// this.addChartCategory(this.contestProblems)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['getContestProblems']),
|
||||||
|
getUserTotalSubmit(username){
|
||||||
|
this.$router.push({
|
||||||
|
name: 'contest-submission-list',
|
||||||
|
query: {username: username}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getUserHomeByUsername(username){
|
||||||
|
|
||||||
|
this.$router.push(
|
||||||
|
{
|
||||||
|
name: 'user-home',
|
||||||
|
query: {username:username}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getContestProblemById(pid){
|
||||||
|
this.$router.push({
|
||||||
|
name: 'contest-problem-details',
|
||||||
|
params: {
|
||||||
|
contestID: this.contestID,
|
||||||
|
problemID: pid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cellClassName ({ row, rowIndex, column, columnIndex }) {
|
||||||
|
if (column.property !== 'id'&&column.property !== 'rating'&&column.property !== 'total_time'&&column.property !=='username') {
|
||||||
|
return row.cellClassName[[this.problems[columnIndex-4].id]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applyToTable (dataRank) {
|
||||||
|
// // deepcopy
|
||||||
|
// let dataRank = JSON.parse(JSON.stringify(data))
|
||||||
|
dataRank.forEach((rank, i) => {
|
||||||
|
let info = rank.submission_info
|
||||||
|
let cellClass = {}
|
||||||
|
Object.keys(info).forEach(problemID => {
|
||||||
|
dataRank[i][problemID] = info[problemID]
|
||||||
|
dataRank[i][problemID].ac_time = time.secondFormat(dataRank[i][problemID].ac_time)
|
||||||
|
let status = info[problemID]
|
||||||
|
if (status.is_first_ac) {
|
||||||
|
cellClass[problemID] = 'first-ac'
|
||||||
|
} else if (status.is_ac) {
|
||||||
|
cellClass[problemID] = 'ac'
|
||||||
|
} else if(status.error_number!=0){
|
||||||
|
cellClass[problemID] = 'wa'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dataRank[i].cellClassName = cellClass
|
||||||
|
})
|
||||||
|
this.dataRank = dataRank
|
||||||
|
},
|
||||||
|
addChartCategory (contestProblems) {
|
||||||
|
let category = []
|
||||||
|
for (let i = 0; i <= contestProblems.length; ++i) {
|
||||||
|
category.push(i)
|
||||||
|
}
|
||||||
|
this.options.yAxis[0].data = category
|
||||||
|
},
|
||||||
|
applyToChart (rankData) {
|
||||||
|
let [users, seriesData] = [[], []]
|
||||||
|
rankData.forEach(rank => {
|
||||||
|
users.push(rank.user.username)
|
||||||
|
let info = rank.submission_info
|
||||||
|
// 提取出已AC题目的时间
|
||||||
|
let timeData = []
|
||||||
|
Object.keys(info).forEach(problemID => {
|
||||||
|
if (info[problemID].is_ac) {
|
||||||
|
timeData.push(info[problemID].ac_time)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
timeData.sort((a, b) => {
|
||||||
|
return a - b
|
||||||
|
})
|
||||||
|
|
||||||
|
let data = []
|
||||||
|
data.push([this.contest.start_time, 0])
|
||||||
|
// index here can be regarded as stacked accepted number count.
|
||||||
|
for (let [index, value] of timeData.entries()) {
|
||||||
|
let realTime = moment(this.contest.start_time).add(value, 'seconds').format()
|
||||||
|
data.push([realTime, index + 1])
|
||||||
|
}
|
||||||
|
seriesData.push({
|
||||||
|
name: rank.user.username,
|
||||||
|
type: 'line',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.options.legend.data = users
|
||||||
|
this.options.series = seriesData
|
||||||
|
},
|
||||||
|
parseTotalTime (totalTime) {
|
||||||
|
return time.secondFormat(totalTime)
|
||||||
|
},
|
||||||
|
downloadRankCSV () {
|
||||||
|
utils.downloadFile(`contest_rank?download_csv=1&contest_id=${this.$route.params.contestID}&force_refrash=${this.forceUpdate ? '1' : '0'}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.echarts {
|
||||||
|
margin: 20px auto;
|
||||||
|
height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 20px!important;
|
||||||
|
padding-top:0px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-full {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#switches p{
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
#switches p:first-child{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
#switches p span {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.vxe-cell p,.vxe-cell span{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
/deep/.vxe-table .vxe-body--column{
|
||||||
|
line-height: 20px!important;
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<template>
|
||||||
|
<div class="problem-list">
|
||||||
|
<vxe-table v-if="contestRuleType == 'ACM' || OIContestRealTimePermission"
|
||||||
|
border="inner"
|
||||||
|
stripe
|
||||||
|
auto-resize
|
||||||
|
:data="problems"
|
||||||
|
@cell-click="goContestProblem">
|
||||||
|
<vxe-table-column field="status" title="" width="50">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-tooltip :content="JUDGE_STATUS[row.status].name" placement="top">
|
||||||
|
<i class="el-icon-check" :style="getIconColor(row.status)" v-if="row.status==0" ></i>
|
||||||
|
<i class="el-icon-minus" :style="getIconColor(row.status)" v-else-if="row.status!=-10" ></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="id" width="80" title="#"></vxe-table-column>
|
||||||
|
<vxe-table-column field="title" title="Title" min-width="350"></vxe-table-column>
|
||||||
|
<vxe-table-column field="ac" title="AC" min-width="80"></vxe-table-column>
|
||||||
|
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||||
|
<vxe-table-column field="ACRating" title="AC Rate" min-width="80"></vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
<!-- <Table v-else
|
||||||
|
:data="problems"
|
||||||
|
:columns="OITableColumns"
|
||||||
|
@on-row-click="goContestProblem"
|
||||||
|
no-data-text="$t('m.No_Problems')"></Table> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState, mapGetters} from 'vuex'
|
||||||
|
import utils from '@/common/utils'
|
||||||
|
import {JUDGE_STATUS} from "@/common/constants"
|
||||||
|
export default {
|
||||||
|
name: 'ContestProblemList',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
contestRuleType:"ACM",
|
||||||
|
JUDGE_STATUS:JUDGE_STATUS,
|
||||||
|
problems:[
|
||||||
|
{status:0,id:'A',title:'测试题目AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',ac:766,total:1000,ACRating:"76.6%"},
|
||||||
|
{status:1,id:'B',title:'测试题目AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',ac:766,total:1000,ACRating:"76.6%"}
|
||||||
|
]
|
||||||
|
// OITableColumns: [
|
||||||
|
// {
|
||||||
|
// title: '#',
|
||||||
|
// key: '_id',
|
||||||
|
// width: 150
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: this.$i18n.t('m.Title'),
|
||||||
|
// key: 'title'
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// this.getContestProblems()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getContestProblems () {
|
||||||
|
this.$store.dispatch('getContestProblems').then(res => {
|
||||||
|
if (this.isAuthenticated) {
|
||||||
|
if (this.contestRuleType === 'ACM') {
|
||||||
|
this.addStatusColumn(this.ACMTableColumns, res.data.data)
|
||||||
|
} else if (this.OIContestRealTimePermission) {
|
||||||
|
this.addStatusColumn(this.ACMTableColumns, res.data.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goContestProblem (event) {
|
||||||
|
console.log(event.row)
|
||||||
|
this.$router.push({
|
||||||
|
name: 'contest-problem-details',
|
||||||
|
params: {
|
||||||
|
contestID: this.$route.params.contestID,
|
||||||
|
problemID: event.row.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getACRate (ACCount, TotalCount) {
|
||||||
|
return utils.getACRate(ACCount, TotalCount)
|
||||||
|
},
|
||||||
|
getIconColor(status){
|
||||||
|
return "font-weight: 600;font-size: 16px;color:"+JUDGE_STATUS[status].rgb;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// ...mapState({
|
||||||
|
// problems: state => state.contest.contestProblems
|
||||||
|
// }),
|
||||||
|
// ...mapGetters(['isAuthenticated', 'contestRuleType', 'OIContestRealTimePermission'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<component :is="currentView"></component>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import ACMContestRank from './ACMContestRank.vue'
|
||||||
|
import OIContestRank from './OIContestRank.vue'
|
||||||
|
|
||||||
|
const NullComponent = {
|
||||||
|
name: 'null-component',
|
||||||
|
template: '<div></div>'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'contest-rank',
|
||||||
|
components: {
|
||||||
|
ACMContestRank,
|
||||||
|
OIContestRank,
|
||||||
|
NullComponent
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['contestRuleType']),
|
||||||
|
currentView () {
|
||||||
|
return 'ACMContestRank'
|
||||||
|
// if (this.contestRuleType === null) {
|
||||||
|
// return 'NullComponent'
|
||||||
|
// }
|
||||||
|
// return this.contestRuleType === 'ACM' ? 'ACMContestRank' : 'OIContestRank'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteLeave (to, from, next) {
|
||||||
|
this.$store.commit("changeContestItemVisible", {menu: true})
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -0,0 +1,485 @@
|
||||||
|
<template>
|
||||||
|
<el-card shadow>
|
||||||
|
<div slot="header"><span class="panel-title">Contest Rank</span>
|
||||||
|
<span style="float:right;font-size: 20px;">
|
||||||
|
<el-popover trigger="hover" placement="left-start">
|
||||||
|
<i class="el-icon-s-tools" slot="reference"></i>
|
||||||
|
<div id="switches">
|
||||||
|
<p>
|
||||||
|
<span>Chart</span>
|
||||||
|
<el-switch v-model="showChart"></el-switch>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Table</span>
|
||||||
|
<el-switch v-model="showTable"></el-switch>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Auto Refresh(10s)</span>
|
||||||
|
<el-switch :disabled="refreshDisabled" @on-change="handleAutoRefresh"></el-switch>
|
||||||
|
</p>
|
||||||
|
<template v-if="isContestAdmin">
|
||||||
|
<p>
|
||||||
|
<span>Force Update</span>
|
||||||
|
<el-switch :disabled="refreshDisabled" v-model="forceUpdate"></el-switch>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template>
|
||||||
|
<el-button type="primary" size="small" @click="downloadRankCSV">Download as CSV</el-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-show="showChart" class="echarts">
|
||||||
|
<ECharts :options="options" ref="chart" :autoresize="true"></ECharts>
|
||||||
|
</div>
|
||||||
|
<div v-show="showTable">
|
||||||
|
<vxe-table
|
||||||
|
round
|
||||||
|
border
|
||||||
|
auto-resize
|
||||||
|
size="small"
|
||||||
|
align="center"
|
||||||
|
:data="dataRank"
|
||||||
|
:cell-class-name="cellClassName"
|
||||||
|
:seq-config="{startIndex: (this.page - 1) * this.limit}"
|
||||||
|
>
|
||||||
|
<vxe-table-column field="id" type="seq" min-width="50" fixed="left"></vxe-table-column>
|
||||||
|
<vxe-table-column field="username" min-width="150" title="User" >
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span><a @click="getUserHomeByUsername(row.user.username)" style="color:rgb(87, 163, 243);">{{row.user.username}}</a>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="total_score" title="TotalScore" min-width="80">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span><a @click="getUserTotalSubmit(row.user.username)" style="color:rgb(87, 163, 243);">{{row.total_score}}</a>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column min-width="80" v-for="problem in problems" :key="problem.id">
|
||||||
|
<template v-slot:header>
|
||||||
|
<span><a @click="getContestProblemById(problem.id)" class="emphasis">{{problem.id}}</a></span>
|
||||||
|
</template>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span v-if="row.submission_info[problem.id]!=-1">{{row.submission_info[problem.id]}}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
</div>
|
||||||
|
<Pagination :total="total"
|
||||||
|
:page-size.sync="limit"
|
||||||
|
:current.sync="page"
|
||||||
|
@on-change="getContestRankData"
|
||||||
|
@on-page-size-change="getContestRankData(1)"
|
||||||
|
show-sizer></Pagination>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
|
||||||
|
import Pagination from '@/components/common/Pagination'
|
||||||
|
import ContestRankMixin from './contestRankMixin'
|
||||||
|
import utils from '@/common/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'acm-contest-rank',
|
||||||
|
components: {
|
||||||
|
Pagination
|
||||||
|
},
|
||||||
|
mixins: [ContestRankMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
refreshDisabled:true,
|
||||||
|
isContestAdmin:true,
|
||||||
|
showChart:true,
|
||||||
|
showTable:true,
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
limit:10,
|
||||||
|
contestID: '',
|
||||||
|
dataRank: [],
|
||||||
|
problems:[],
|
||||||
|
options: {
|
||||||
|
|
||||||
|
title: {
|
||||||
|
text: 'Top 10 Teams',
|
||||||
|
left: 'center'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
show: true,
|
||||||
|
feature: {
|
||||||
|
dataView: {show: true, readOnly: true},
|
||||||
|
magicType: {show: true, type: ['line', 'bar']},
|
||||||
|
saveAsImage: {show: true}
|
||||||
|
},
|
||||||
|
right: '10%',
|
||||||
|
top:'5%'
|
||||||
|
},
|
||||||
|
calculable: true,
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: ['root'],
|
||||||
|
boundaryGap: true,
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0,
|
||||||
|
showMinLabel: true,
|
||||||
|
showMaxLabel: true,
|
||||||
|
align: 'center',
|
||||||
|
formatter: (value, index) => {
|
||||||
|
return utils.breakLongWords(value, 14)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
alignWithLabel: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
grid: {
|
||||||
|
left:'11%'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Score',
|
||||||
|
type: 'bar',
|
||||||
|
barMaxWidth: '80',
|
||||||
|
data: [0],
|
||||||
|
markPoint: {
|
||||||
|
data: [
|
||||||
|
{type: 'max', name: 'max'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.contestID = this.$route.params.contestID
|
||||||
|
// this.getContestRankData(1)
|
||||||
|
// if (this.contestProblems.length === 0) {
|
||||||
|
// this.getContestProblems().then((res) => {
|
||||||
|
// this.addTableColumns(res.data.data)
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// this.addTableColumns(this.contestProblems)
|
||||||
|
// }
|
||||||
|
let dataRank=[{
|
||||||
|
user:{
|
||||||
|
username:'user1',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:9999,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user2',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user3',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user4',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user5',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user6',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user7',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user8',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user9',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user:{
|
||||||
|
username:'user10',
|
||||||
|
id:'id',
|
||||||
|
},
|
||||||
|
total_score:430,
|
||||||
|
submission_info:{
|
||||||
|
'A':100,
|
||||||
|
'B':-1,
|
||||||
|
'C':100,
|
||||||
|
'D':60,
|
||||||
|
'E':70,
|
||||||
|
'F':100,
|
||||||
|
'G':0,
|
||||||
|
'H':-1,
|
||||||
|
'I':-1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
this.applyToTable(dataRank);
|
||||||
|
this.applyToChart(dataRank);
|
||||||
|
this.problems =[
|
||||||
|
{id:'A'},
|
||||||
|
{id:'B'},
|
||||||
|
{id:'C'},
|
||||||
|
{id:'D'},
|
||||||
|
{id:'E'},
|
||||||
|
{id:'F'},
|
||||||
|
{id:'G'},
|
||||||
|
{id:'H'},
|
||||||
|
{id:'I'},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['getContestProblems']),
|
||||||
|
|
||||||
|
cellClassName ({ row, rowIndex, column, columnIndex }) {
|
||||||
|
if (column.property !== 'id'&&column.property !== 'total_score'&&column.property !=='username') {
|
||||||
|
return row.cellClassName[[this.problems[columnIndex-3].id]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getUserTotalSubmit(username){
|
||||||
|
this.$router.push({
|
||||||
|
name: 'contest-submission-list',
|
||||||
|
query: {username: username}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getUserHomeByUsername(username){
|
||||||
|
|
||||||
|
this.$router.push(
|
||||||
|
{
|
||||||
|
name: 'user-home',
|
||||||
|
query: {username:username}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getContestProblemById(pid){
|
||||||
|
this.$router.push({
|
||||||
|
name: 'contest-problem-details',
|
||||||
|
params: {
|
||||||
|
contestID: this.contestID,
|
||||||
|
problemID: pid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
applyToChart (rankData) {
|
||||||
|
let [usernames, scores] = [[], []]
|
||||||
|
rankData.forEach(ele => {
|
||||||
|
usernames.push(ele.user.username)
|
||||||
|
scores.push(ele.total_score)
|
||||||
|
})
|
||||||
|
this.options.xAxis[0].data = usernames
|
||||||
|
this.options.series[0].data = scores
|
||||||
|
},
|
||||||
|
applyToTable (dataRank) {
|
||||||
|
// let dataRank = JSON.parse(JSON.stringify(data))
|
||||||
|
|
||||||
|
dataRank.forEach((rank, i) => {
|
||||||
|
let info = rank.submission_info
|
||||||
|
let cellClass = {}
|
||||||
|
Object.keys(info).forEach(problemID => {
|
||||||
|
dataRank[i][problemID] = info[problemID]
|
||||||
|
let score = info[problemID]
|
||||||
|
if(score==0){
|
||||||
|
cellClass[problemID] = 'oi-0';
|
||||||
|
}else if(score>0&&score<100){
|
||||||
|
cellClass[problemID] = 'oi-between';
|
||||||
|
}else if(score==100){
|
||||||
|
cellClass[problemID] = 'oi-100';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dataRank[i].cellClassName = cellClass
|
||||||
|
})
|
||||||
|
this.dataRank = dataRank
|
||||||
|
},
|
||||||
|
|
||||||
|
downloadRankCSV () {
|
||||||
|
utils.downloadFile(`contest_rank?download_csv=1&contest_id=${this.$route.params.contestID}&force_refrash=${this.forceUpdate ? '1' : '0'}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.echarts {
|
||||||
|
margin: 20px auto;
|
||||||
|
height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 20px!important;
|
||||||
|
padding-top:0!important;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-full {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#switches p{
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
#switches p:first-child{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
#switches p span {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.vxe-cell p,.vxe-cell span{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
/deep/.vxe-table .vxe-body--column{
|
||||||
|
line-height: 20px!important;
|
||||||
|
padding: 8px!important;
|
||||||
|
}
|
||||||
|
a.emphasis{
|
||||||
|
color:#495060
|
||||||
|
}
|
||||||
|
a.emphasis:hover{
|
||||||
|
color:#2d8cf0
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,111 @@
|
||||||
|
import api from '@/common/api'
|
||||||
|
import { mapGetters, mapState } from 'vuex'
|
||||||
|
import { CONTEST_STATUS } from '@/common/constants'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
getContestRankData (page = 1, refresh = false) {
|
||||||
|
let offset = (page - 1) * this.limit
|
||||||
|
if (this.showChart && !refresh) {
|
||||||
|
this.$refs.chart.showLoading({maskColor: 'rgba(250, 250, 250, 0.8)'})
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
offset,
|
||||||
|
limit: this.limit,
|
||||||
|
contest_id: this.$route.params.contestID,
|
||||||
|
force_refresh: this.forceUpdate ? '1' : '0'
|
||||||
|
}
|
||||||
|
api.getContestRank(params).then(res => {
|
||||||
|
if (this.showChart && !refresh) {
|
||||||
|
this.$refs.chart.hideLoading()
|
||||||
|
}
|
||||||
|
this.total = res.data.data.total
|
||||||
|
if (page === 1) {
|
||||||
|
this.applyToChart(res.data.data.results.slice(0, 10))
|
||||||
|
}
|
||||||
|
this.applyToTable(res.data.data.results)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleAutoRefresh (status) {
|
||||||
|
if (status === true) {
|
||||||
|
this.refreshFunc = setInterval(() => {
|
||||||
|
this.page = 1
|
||||||
|
this.getContestRankData(1, true)
|
||||||
|
}, 10000)
|
||||||
|
} else {
|
||||||
|
clearInterval(this.refreshFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// ...mapGetters(['isContestAdmin']),
|
||||||
|
// ...mapState({
|
||||||
|
// 'contest': state => state.contest.contest,
|
||||||
|
// 'contestProblems': state => state.contest.contestProblems
|
||||||
|
// }),
|
||||||
|
// showChart: {
|
||||||
|
// get () {
|
||||||
|
// return this.$store.state.contest.itemVisible.chart
|
||||||
|
// },
|
||||||
|
// set (value) {
|
||||||
|
// this.$store.commit('changeContestItemVisible', {chart: value})
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// showMenu: {
|
||||||
|
// get () {
|
||||||
|
// return this.$store.state.contest.itemVisible.menu
|
||||||
|
// },
|
||||||
|
// set (value) {
|
||||||
|
// this.$store.commit('changeContestItemVisible', {menu: value})
|
||||||
|
// this.$nextTick(() => {
|
||||||
|
// if (this.showChart) {
|
||||||
|
// this.$refs.chart.resize()
|
||||||
|
// }
|
||||||
|
// this.$refs.tableRank.handleResize()
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// showRealName: {
|
||||||
|
// get () {
|
||||||
|
// return this.$store.state.contest.itemVisible.realName
|
||||||
|
// },
|
||||||
|
// set (value) {
|
||||||
|
// this.$store.commit('changeContestItemVisible', {realName: value})
|
||||||
|
// if (value) {
|
||||||
|
// this.columns.splice(2, 0, {
|
||||||
|
// title: 'RealName',
|
||||||
|
// align: 'center',
|
||||||
|
// width: 150,
|
||||||
|
// render: (h, {row}) => {
|
||||||
|
// return h('span', row.user.real_name)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// this.columns.splice(2, 1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
forceUpdate: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state.contest.forceUpdate
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit('changeRankForceUpdate', {value: value})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// limit: {
|
||||||
|
// get () {
|
||||||
|
// return this.$store.state.contest.rankLimit
|
||||||
|
// },
|
||||||
|
// set (value) {
|
||||||
|
// this.$store.commit('changeContestRankLimit', {rankLimit: value})
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// refreshDisabled () {
|
||||||
|
// return this.contest.status === CONTEST_STATUS.ENDED
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
clearInterval(this.refreshFunc)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,83 +0,0 @@
|
||||||
<template>
|
|
||||||
<span>
|
|
||||||
<slot>{{content}}</slot>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "CountDown",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
timer: null,
|
|
||||||
date: null,
|
|
||||||
savedtime: 0, //时间
|
|
||||||
hour: null,
|
|
||||||
min: null,
|
|
||||||
sec: null,
|
|
||||||
content: this.endText //显示
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
// 倒计时时间 (分钟)
|
|
||||||
endTime: {
|
|
||||||
type: Number,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
endText: {
|
|
||||||
type: String,
|
|
||||||
default: "0:00:00"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// 时间换成毫秒传递
|
|
||||||
this.timeStart(this.endTime * 60000);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 起始时间
|
|
||||||
timeStart(endTime) {
|
|
||||||
this.date = new Date();
|
|
||||||
var date1 = new Date().getTime(); // 获取当前时间戳
|
|
||||||
// 当前时间戳+3600s(一小时,其他时间通过计算时间戳进行相应加减),重新设置 Date 对象
|
|
||||||
this.date.setTime(date1 + endTime);
|
|
||||||
this.date = this.date.getTime();
|
|
||||||
// 传递结束时的时间戳
|
|
||||||
this.countdowm(this.date);
|
|
||||||
},
|
|
||||||
// 继续倒计时
|
|
||||||
timeresume() {
|
|
||||||
this.timeStart(this.savedtime);
|
|
||||||
},
|
|
||||||
// 暂停时间
|
|
||||||
timepause() {
|
|
||||||
clearInterval(this.timer);
|
|
||||||
this.savedtime =
|
|
||||||
this.hour * 60 * 60 * 1000 + this.min * 60 * 1000 + this.sec * 1000;
|
|
||||||
},
|
|
||||||
// 开始倒计时
|
|
||||||
countdowm(timestamp) {
|
|
||||||
let self = this;
|
|
||||||
self.timer = setInterval(function() {
|
|
||||||
let nowTime = new Date();
|
|
||||||
let endTime = new Date(timestamp * 1);
|
|
||||||
let t = endTime.getTime() - nowTime.getTime();
|
|
||||||
// 判断剩余时间是否 >0
|
|
||||||
if (t > 0) {
|
|
||||||
self.hour = Math.floor((t / 3600000) % 24);
|
|
||||||
self.min = Math.floor((t / 60000) % 60);
|
|
||||||
self.sec = Math.floor((t / 1000) % 60);
|
|
||||||
self.$emit("callBack", 1 + self.min + self.hour * 60); // 每减少一分钟父页面滑块的值就减 1
|
|
||||||
let min = self.min < 10 ? "0" + self.min : self.min;
|
|
||||||
let sec = self.sec < 10 ? "0" + self.sec : self.sec;
|
|
||||||
let format = `${self.hour}:${min}:${sec}`;
|
|
||||||
self.content = format;
|
|
||||||
} else {
|
|
||||||
// 倒计时结束
|
|
||||||
self.$emit("callBack", 0);
|
|
||||||
clearInterval(self.timer);
|
|
||||||
self.content = "0:00:00";
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,82 +0,0 @@
|
||||||
<template>
|
|
||||||
|
|
||||||
<div class="item">
|
|
||||||
<div class="open_con">
|
|
||||||
<el-button
|
|
||||||
icon="el-icon-switch-button"
|
|
||||||
circle
|
|
||||||
:plain="plain"
|
|
||||||
type="primary"
|
|
||||||
@click="OPens"
|
|
||||||
></el-button>
|
|
||||||
</div>
|
|
||||||
<div class="slider">
|
|
||||||
<el-slider v-model="value" :format-tooltip="formatTooltip"></el-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import countDown from "./count"; //引入倒计时组件
|
|
||||||
export default {
|
|
||||||
name: "Caeds",
|
|
||||||
components: { countDown },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
value: 0,
|
|
||||||
plain: true, // 开关按钮 true 是关闭,false是打开
|
|
||||||
stop: 0,
|
|
||||||
time: "0:00:00"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {
|
|
||||||
formatTooltip(val) {
|
|
||||||
if (val < 60) {
|
|
||||||
if (val < 10) {
|
|
||||||
this.time = "0:0" + val + ":00";
|
|
||||||
return "0:0" + val + ":00";
|
|
||||||
} else {
|
|
||||||
this.time = "0:" + val + ":00";
|
|
||||||
return "0:" + val + ":00";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (val < 70) {
|
|
||||||
this.time = "1:0" + (val - 60) + ":00";
|
|
||||||
return "1:0" + (val - 60) + ":00";
|
|
||||||
} else {
|
|
||||||
this.time = "1:" + (val - 60) + ":00";
|
|
||||||
return "1:" + (val - 60) + ":00";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//
|
|
||||||
callBack(val) {
|
|
||||||
console.log(val);
|
|
||||||
this.value = val;
|
|
||||||
// 倒计时结束 关闭按钮
|
|
||||||
if (val == 0) {
|
|
||||||
this.stop = 0;
|
|
||||||
this.plain = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
OPens() {
|
|
||||||
if (this.value != 0) {
|
|
||||||
if (!this.plain && this.stop == 1) {
|
|
||||||
this.stop = 2;
|
|
||||||
console.log("stop");
|
|
||||||
this.$refs.countdown.timepause();
|
|
||||||
}
|
|
||||||
if (this.plain && this.stop == 2) {
|
|
||||||
this.stop = 1;
|
|
||||||
console.log("open");
|
|
||||||
this.$refs.countdown.timeresume();
|
|
||||||
}
|
|
||||||
if (this.stop == 0) {
|
|
||||||
this.stop = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.plain = !this.plain;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -34,29 +34,38 @@
|
||||||
<vxe-table
|
<vxe-table
|
||||||
border="inner"
|
border="inner"
|
||||||
stripe
|
stripe
|
||||||
|
auto-resize
|
||||||
@cell-mouseenter="cellHover"
|
@cell-mouseenter="cellHover"
|
||||||
:data="problemList">
|
:data="problemList">
|
||||||
<vxe-table-column field="pid" title="Problem ID" width="100"></vxe-table-column>
|
<vxe-table-column field="status" title="" width="30">
|
||||||
|
<template v-slot="{ row }" >
|
||||||
|
<el-tooltip :content="JUDGE_STATUS[row.myStatus].name" placement="top" >
|
||||||
|
<i class="el-icon-check" :style="getIconColor(row.myStatus)" v-if="row.myStatus==0" ></i>
|
||||||
|
<i class="el-icon-minus" :style="getIconColor(row.myStatus)" v-else-if="row.myStatus!=-10" ></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="pid" title="Problem ID" min-width="100"></vxe-table-column>
|
||||||
|
|
||||||
<vxe-table-column field="title" title="Title" width="300">
|
<vxe-table-column field="title" title="Title" min-width="250">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<a :href="getProblemUri(row.pid)" style="color:rgb(87, 163, 243);">{{row.title}}</a>
|
<a :href="getProblemUri(row.pid)" style="color:rgb(87, 163, 243);font-size: 14px;">{{row.title}}</a>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
|
|
||||||
<vxe-table-column field="level" title="Level" width="150">
|
<vxe-table-column field="level" title="Level" min-width="100">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<span :class="getLevelColor(row.level)">{{PROBLEM_LEVEL[row.level].name}}</span>
|
<span :class="getLevelColor(row.level)">{{PROBLEM_LEVEL[row.level].name}}</span>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
|
|
||||||
<vxe-table-column field="tag" title="Tag" width="250" type="html">
|
<vxe-table-column field="tag" title="Tag" min-width="200">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<span class="el-tag el-tag--medium el-tag--light is-hit" style="margin-right: 7px;" v-for="tag in row.tags" :key="tag">{{tag}}</span>
|
<span class="el-tag el-tag--medium el-tag--light is-hit" style="margin-right: 7px;margin-top:4px" v-for="tag in row.tags" :key="tag">{{tag}}</span>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
<vxe-table-column field="total" title="Total" width="100"></vxe-table-column>
|
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="ACRate" title="AC Rate" width="100"></vxe-table-column>
|
<vxe-table-column field="ACRate" title="AC Rate" min-width="80"></vxe-table-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
<Pagination :total="total" :page-size="limit" @on-change="pushRouter" :current.sync="query.page"></Pagination>
|
<Pagination :total="total" :page-size="limit" @on-change="pushRouter" :current.sync="query.page"></Pagination>
|
||||||
|
@ -117,25 +126,25 @@
|
||||||
name:'模拟题'
|
name:'模拟题'
|
||||||
}],
|
}],
|
||||||
problemRecord:[
|
problemRecord:[
|
||||||
{status:'0',count:'70'},
|
{status:'0',count:70},
|
||||||
{status:'-1',count:'70'},
|
{status:'-1',count:70},
|
||||||
{status:'3',count:'70'},
|
{status:'3',count:70},
|
||||||
{status:'1',count:'70'},
|
{status:'1',count:70},
|
||||||
{status:'4',count:'70'},
|
{status:'4',count:70},
|
||||||
{status:'-3',count:'70'},
|
{status:'-3',count:70},
|
||||||
{status:'-2',count:'70'},
|
{status:'-2',count:70},
|
||||||
{status:'5',count:'70'},
|
{status:'5',count:70},
|
||||||
],
|
],
|
||||||
problemList: [
|
problemList: [
|
||||||
{pid:'1000',title:'测试标题',level:0,
|
{myStatus:-10,pid:'1000',title:'测试标题',level:0,
|
||||||
tags:['简单题','模拟题'],
|
tags:['简单题','模拟题'],
|
||||||
total:'10000',ACRate:'59.12%'
|
total:'10000',ACRate:'59.12%'
|
||||||
},
|
},
|
||||||
{pid:'1000',title:'测试标题',level:1,
|
{myStatus:-1,pid:'1000',title:'测试标题',level:1,
|
||||||
tags:['简单题','模拟题'],
|
tags:['简单题','模拟题','递归题','递归题'],
|
||||||
total:'10000',ACRate:'59.12%'
|
total:'10000',ACRate:'59.12%'
|
||||||
},
|
},
|
||||||
{pid:'1000',title:'测试标题',level:2,
|
{myStatus:0,pid:'1000',title:'测试标题',level:2,
|
||||||
tags:['简单题','模拟题'],
|
tags:['简单题','模拟题'],
|
||||||
total:'10000',ACRate:'59.12%'
|
total:'10000',ACRate:'59.12%'
|
||||||
},
|
},
|
||||||
|
@ -239,6 +248,9 @@
|
||||||
},
|
},
|
||||||
getLevelColor(level){
|
getLevelColor(level){
|
||||||
return 'el-tag el-tag--small status-'+PROBLEM_LEVEL[level].color;
|
return 'el-tag el-tag--small status-'+PROBLEM_LEVEL[level].color;
|
||||||
|
},
|
||||||
|
getIconColor(status){
|
||||||
|
return "font-weight: 600;font-size: 16px;color:"+JUDGE_STATUS[status].rgb;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<el-row type="flex" justify="space-around">
|
<el-row type="flex" justify="space-around">
|
||||||
<el-col :span="22">
|
<el-col :span="24">
|
||||||
<el-card :padding="10">
|
<el-card :padding="10">
|
||||||
<div slot="header"><span class="panel-title">ACM Ranklist</span></div>
|
<div slot="header"><span class="panel-title">ACM Ranklist</span></div>
|
||||||
<div class="echarts">
|
<div class="echarts">
|
||||||
<ECharts :options="options" ref="chart" auto-resize></ECharts>
|
<ECharts :options="options" ref="chart" :autoresize="true"></ECharts>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row style="font-weight: 500;">
|
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row auto-resize style="font-weight: 500;">
|
||||||
<vxe-table-column type="seq" width="100"></vxe-table-column>
|
<vxe-table-column type="seq" min-width="50"></vxe-table-column>
|
||||||
<vxe-table-column field="username" title="User" width="197">
|
<vxe-table-column field="username" title="User" min-width="150">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
<vxe-table-column field="nickname" title="Nickname" width="197"></vxe-table-column>
|
<vxe-table-column field="nickname" title="Nickname" min-width="180"></vxe-table-column>
|
||||||
<vxe-table-column field="signature" title="Mood" width="197"></vxe-table-column>
|
<vxe-table-column field="signature" title="Mood" min-width="180"></vxe-table-column>
|
||||||
<vxe-table-column field="ac" title="AC" width="197"></vxe-table-column>
|
<vxe-table-column field="ac" title="AC" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="total" title="Total" width="197"></vxe-table-column>
|
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="rating" title="Rating" width="197"></vxe-table-column>
|
<vxe-table-column field="rating" title="Rating" min-width="80"></vxe-table-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
|
|
||||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||||
|
@ -45,12 +45,7 @@
|
||||||
limit: 30,
|
limit: 30,
|
||||||
total: 0,
|
total: 0,
|
||||||
loadingTable: false,
|
loadingTable: false,
|
||||||
dataRank: [
|
dataRank: [],
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
|
||||||
|
|
||||||
],
|
|
||||||
options: {
|
options: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
|
@ -60,7 +55,9 @@
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
x: '3%',
|
x: '3%',
|
||||||
x2: '3%'
|
x2: '3%',
|
||||||
|
left:'8%',
|
||||||
|
right:'8%'
|
||||||
},
|
},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -69,7 +66,7 @@
|
||||||
magicType: {show: true, type: ['line', 'bar', 'stack']},
|
magicType: {show: true, type: ['line', 'bar', 'stack']},
|
||||||
saveAsImage: {show: true}
|
saveAsImage: {show: true}
|
||||||
},
|
},
|
||||||
right: '5%',
|
right: '8%',
|
||||||
top:'5%'
|
top:'5%'
|
||||||
},
|
},
|
||||||
calculable: true,
|
calculable: true,
|
||||||
|
@ -83,14 +80,20 @@
|
||||||
showMaxLabel: true,
|
showMaxLabel: true,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
formatter: (value, index) => {
|
formatter: (value, index) => {
|
||||||
return utils.breakLongWords(value, 10)
|
return utils.breakLongWords(value, 13)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value'
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
rotate:50,
|
||||||
|
textStyle:{
|
||||||
|
fontSize:'12em',
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
|
@ -120,6 +123,21 @@
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
// this.getRankData(1)
|
// this.getRankData(1)
|
||||||
|
let dataRank = [
|
||||||
|
{username:'20180308010541111',nickname:'Himit_ZHsssssssssssssssss我说你啵啵啵啵啵啵',signature:'Show me your code',ac:9999,total:9999,rating:'99.99%'},
|
||||||
|
{username:'测试名字为什么要那么长的呢',nickname:'图sddddddddd',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'20180308010541111',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'测试名字为什么要那么长你有病啊',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'测试名字为什么要那么长',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',ac:100,total:100,rating:'100%'},
|
||||||
|
|
||||||
|
];
|
||||||
|
this.dataRank = dataRank;
|
||||||
|
this.changeCharts(dataRank);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getRankData (page) {
|
getRankData (page) {
|
||||||
|
@ -143,9 +161,9 @@
|
||||||
changeCharts (rankData) {
|
changeCharts (rankData) {
|
||||||
let [usernames, acData, totalData] = [[], [], []]
|
let [usernames, acData, totalData] = [[], [], []]
|
||||||
rankData.forEach(ele => {
|
rankData.forEach(ele => {
|
||||||
usernames.push(ele.user.username)
|
usernames.push(ele.username)
|
||||||
acData.push(ele.accepted_number)
|
acData.push(ele.ac)
|
||||||
totalData.push(ele.submission_number)
|
totalData.push(ele.total)
|
||||||
})
|
})
|
||||||
this.options.xAxis[0].data = usernames
|
this.options.xAxis[0].data = usernames
|
||||||
this.options.series[0].data = acData
|
this.options.series[0].data = acData
|
||||||
|
@ -154,6 +172,15 @@
|
||||||
getInfoByUsername(username){
|
getInfoByUsername(username){
|
||||||
return '/user-home/'+username;
|
return '/user-home/'+username;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
getFontSize(res){
|
||||||
|
let docEl = document.documentElement,
|
||||||
|
clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
|
||||||
|
if (!clientWidth) return;
|
||||||
|
let fontSize = 100 * (clientWidth / 1920);
|
||||||
|
return res*fontSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -161,7 +188,12 @@
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.echarts {
|
.echarts {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 95%;
|
width: 100%;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<el-row type="flex" justify="space-around">
|
<el-row type="flex" justify="space-around">
|
||||||
<el-col :span="22">
|
<el-col :span="24">
|
||||||
<el-card :padding="10">
|
<el-card :padding="10">
|
||||||
<div slot="header"><span class="panel-title">OI Ranklist</span></div>
|
<div slot="header"><span class="panel-title">OI Ranklist</span></div>
|
||||||
<div class="echarts">
|
<div class="echarts">
|
||||||
<ECharts :options="options" ref="chart" auto-resize></ECharts>
|
<ECharts :options="options" ref="chart" auto-resize></ECharts>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row style="font-weight: 500;">
|
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row auto-resize style="font-weight: 500;">
|
||||||
<vxe-table-column type="seq" width="100"></vxe-table-column>
|
<vxe-table-column type="seq" min-width="50"></vxe-table-column>
|
||||||
<vxe-table-column field="username" title="User" width="168">
|
<vxe-table-column field="username" title="User" min-width="150">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
<vxe-table-column field="nickname" title="Nickname" width="168"></vxe-table-column>
|
<vxe-table-column field="nickname" title="Nickname" min-width="180"></vxe-table-column>
|
||||||
<vxe-table-column field="signature" title="Mood" width="173"></vxe-table-column>
|
<vxe-table-column field="signature" title="Mood" min-width="180"></vxe-table-column>
|
||||||
<vxe-table-column field="score" title="Score" width="168"></vxe-table-column>
|
<vxe-table-column field="score" title="Score" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="ac" title="AC" width="168"></vxe-table-column>
|
<vxe-table-column field="ac" title="AC" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="total" title="Total" width="168"></vxe-table-column>
|
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||||
<vxe-table-column field="rating" title="Rating" width="168"></vxe-table-column>
|
<vxe-table-column field="rating" title="Rating" min-width="80"></vxe-table-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||||
@on-change="getRankData"
|
@on-change="getRankData"
|
||||||
|
@ -44,11 +44,7 @@
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
total: 0,
|
total: 0,
|
||||||
dataRank: [
|
dataRank:[],
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
|
||||||
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
|
||||||
],
|
|
||||||
options: {
|
options: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
|
@ -58,7 +54,9 @@
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
x: '3%',
|
x: '3%',
|
||||||
x2: '3%'
|
x2: '3%',
|
||||||
|
left:'8%',
|
||||||
|
right:'8%'
|
||||||
},
|
},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -67,8 +65,8 @@
|
||||||
magicType: {show: true, type: ['line', 'bar']},
|
magicType: {show: true, type: ['line', 'bar']},
|
||||||
saveAsImage: {show: true}
|
saveAsImage: {show: true}
|
||||||
},
|
},
|
||||||
right: '5%',
|
right: '8%',
|
||||||
top:'5%'
|
top:'5%',
|
||||||
},
|
},
|
||||||
calculable: true,
|
calculable: true,
|
||||||
xAxis: [
|
xAxis: [
|
||||||
|
@ -92,7 +90,13 @@
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value'
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
rotate:50,
|
||||||
|
textStyle:{
|
||||||
|
fontSize:'12em',
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
|
@ -113,6 +117,20 @@
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
// this.getRankData(1)
|
// this.getRankData(1)
|
||||||
|
let data = [
|
||||||
|
{username:'root111',nickname:'Himit_ZH',signature:'Show me your code',score:100000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'测试一下不会真的有人取那么长的名字吧',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
{username:'root',nickname:'Himit_ZH',signature:'Show me your code',score:10000,ac:100,total:100,rating:'100%'},
|
||||||
|
];
|
||||||
|
this.dataRank = data;
|
||||||
|
this.changeCharts(data);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getRankData (page) {
|
getRankData (page) {
|
||||||
|
@ -131,8 +149,8 @@
|
||||||
changeCharts (rankData) {
|
changeCharts (rankData) {
|
||||||
let [usernames, scores] = [[], []]
|
let [usernames, scores] = [[], []]
|
||||||
rankData.forEach(ele => {
|
rankData.forEach(ele => {
|
||||||
usernames.push(ele.user.username)
|
usernames.push(ele.username)
|
||||||
scores.push(ele.total_score)
|
scores.push(ele.score)
|
||||||
})
|
})
|
||||||
this.options.xAxis[0].data = usernames
|
this.options.xAxis[0].data = usernames
|
||||||
this.options.series[0].data = scores
|
this.options.series[0].data = scores
|
||||||
|
@ -147,7 +165,12 @@
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.echarts {
|
.echarts {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 95%;
|
width: 100%;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,14 +21,14 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col v-if="submission.data && !isCE" :span="22">
|
<el-col v-if="submission.data && !isCE" :span="22">
|
||||||
<vxe-table align="center" :data="submission.data" stripe style="padding-top: 13px;">
|
<vxe-table align="center" :data="submission.data" stripe auto-resize style="padding-top: 13px;">
|
||||||
<vxe-table-column field="sid" title="ID" width="213"></vxe-table-column>
|
<vxe-table-column field="sid" title="ID" min-width="100"></vxe-table-column>
|
||||||
<vxe-table-column
|
<vxe-table-column
|
||||||
field="stime"
|
field="stime"
|
||||||
title="Submit time"
|
title="Submit time"
|
||||||
width="213"
|
min-width="150"
|
||||||
></vxe-table-column>
|
></vxe-table-column>
|
||||||
<vxe-table-column field="pid" title="Problem ID" width="213">
|
<vxe-table-column field="pid" title="Problem ID" min-width="100">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<a
|
<a
|
||||||
:href="getProblemUri(row.pid)"
|
:href="getProblemUri(row.pid)"
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
<vxe-table-column field="status" title="Status" width="213">
|
<vxe-table-column field="status" title="Status" min-width="100">
|
||||||
<template v-slot="{ row }">
|
<template v-slot="{ row }">
|
||||||
<span :class="getStatusColor(row.status)">{{
|
<span :class="getStatusColor(row.status)">{{
|
||||||
JUDGE_STATUS[row.status].name
|
JUDGE_STATUS[row.status].name
|
||||||
|
@ -47,12 +47,12 @@
|
||||||
<vxe-table-column
|
<vxe-table-column
|
||||||
field="time"
|
field="time"
|
||||||
title="Time"
|
title="Time"
|
||||||
width="213"
|
min-width="64"
|
||||||
></vxe-table-column>
|
></vxe-table-column>
|
||||||
<vxe-table-column
|
<vxe-table-column
|
||||||
field="memory"
|
field="memory"
|
||||||
title="Memory"
|
title="Memory"
|
||||||
width="213"
|
min-width="96"
|
||||||
></vxe-table-column>
|
></vxe-table-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
@ -120,8 +120,8 @@ export default {
|
||||||
stime: "2020-08-08 16:00:00",
|
stime: "2020-08-08 16:00:00",
|
||||||
pid: "1001",
|
pid: "1001",
|
||||||
status: 0,
|
status: 0,
|
||||||
time: "4ms",
|
time: "1000ms",
|
||||||
memory: "3MB",
|
memory: "9999MB",
|
||||||
language:'Java',
|
language:'Java',
|
||||||
author: "Himit_ZH",
|
author: "Himit_ZH",
|
||||||
err_info:'CE错误',
|
err_info:'CE错误',
|
||||||
|
|
|
@ -2,348 +2,421 @@
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<el-card shadow>
|
<el-card shadow>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :md="2" :lg="2">
|
<el-col :md="2" :lg="2">
|
||||||
<span class="panel-title hidden-xs-only" >Status</span>
|
<span class="panel-title hidden-xs-only">Status</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :xs="16" :md="10" :lg="10">
|
<el-col :xs="16" :md="10" :lg="10">
|
||||||
<el-switch
|
<el-switch
|
||||||
style="display: block"
|
style="display: block"
|
||||||
v-model="formFilter.myself"
|
v-model="formFilter.myself"
|
||||||
active-color="#ff4949"
|
active-color="#ff4949"
|
||||||
active-text="Mine"
|
active-text="Mine"
|
||||||
:width="40"
|
:width="40"
|
||||||
inactive-text="All">
|
inactive-text="All"
|
||||||
</el-switch>
|
>
|
||||||
</el-col>
|
</el-switch>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
<el-col :xs="8" :md="4" :lg="4">
|
<el-col :xs="8" :md="4" :lg="4">
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
class="drop-menu"
|
class="drop-menu"
|
||||||
@command="handleStatusChange"
|
@command="handleStatusChange"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
{{formFilter.status === '' ? 'Status' : formFilter.status}}
|
{{ formFilter.status === "" ? "Status" : formFilter.status }}
|
||||||
<i class="el-icon-caret-bottom"></i>
|
<i class="el-icon-caret-bottom"></i>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="All">All</el-dropdown-item>
|
<el-dropdown-item command="All">All</el-dropdown-item>
|
||||||
<el-dropdown-item v-for="result in Object.keys(JUDGE_STATUS)" :key="result" :command="result">
|
<el-dropdown-item
|
||||||
{{JUDGE_STATUS[result].name}}
|
v-for="result in Object.keys(JUDGE_STATUS)"
|
||||||
</el-dropdown-item>
|
:key="result"
|
||||||
</el-dropdown-menu>
|
:command="result"
|
||||||
</el-dropdown>
|
>
|
||||||
</el-col>
|
{{ JUDGE_STATUS[result].name }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
<el-col :xs="24" :md="4" :lg="4" class="search">
|
<el-col :xs="24" :md="4" :lg="4" class="search">
|
||||||
<vxe-input v-model="formFilter.pid" placeholder="Enter Problem ID" type="search" size="medium" @search-click="filterByKeyword"></vxe-input>
|
<vxe-input
|
||||||
</el-col>
|
v-model="formFilter.pid"
|
||||||
<el-col :xs="24" :md="4" :lg="4" class="search">
|
placeholder="Enter Problem ID"
|
||||||
<vxe-input v-model="formFilter.pid" placeholder="Enter Author" type="search" size="medium" @search-click="filterByKeyword"></vxe-input>
|
type="search"
|
||||||
</el-col>
|
size="medium"
|
||||||
</el-row>
|
@search-click="filterByKeyword"
|
||||||
</div>
|
></vxe-input>
|
||||||
<vxe-table
|
</el-col>
|
||||||
border="inner"
|
<el-col :xs="24" :md="4" :lg="4" class="search">
|
||||||
stripe
|
<vxe-input
|
||||||
highlight-current-row
|
v-model="formFilter.pid"
|
||||||
highlight-hover-row
|
placeholder="Enter Author"
|
||||||
align="center"
|
type="search"
|
||||||
:row-class-name="tableRowClassName"
|
size="medium"
|
||||||
:data="submissions">
|
@search-click="filterByKeyword"
|
||||||
<vxe-table-column field="sid" title="ID" width="150"></vxe-table-column>
|
></vxe-input>
|
||||||
<vxe-table-column field="stime" title="Submit time" width="150"></vxe-table-column>
|
</el-col>
|
||||||
<vxe-table-column field="pid" title="Problem ID" width="150">
|
</el-row>
|
||||||
<template v-slot="{ row }">
|
</div>
|
||||||
<a :href="getProblemUri(row.pid)" style="color:rgb(87, 163, 243);">{{row.pid}}</a>
|
<vxe-table
|
||||||
</template>
|
border="inner"
|
||||||
</vxe-table-column>
|
stripe
|
||||||
<vxe-table-column field="status" title="Status" width="150">
|
highlight-current-row
|
||||||
<template v-slot="{ row }">
|
highlight-hover-row
|
||||||
<span :class="getStatusColor(row.status)">{{JUDGE_STATUS[row.status].name}}</span>
|
align="center"
|
||||||
</template>
|
auto-resize
|
||||||
</vxe-table-column>
|
:row-class-name="tableRowClassName"
|
||||||
<vxe-table-column field="time" title="Time" width="150"></vxe-table-column>
|
:data="submissions"
|
||||||
<vxe-table-column field="memory" title="Memory" width="150"></vxe-table-column>
|
>
|
||||||
<vxe-table-column field="language" title="Language" width="150">
|
<vxe-table-column
|
||||||
<template v-slot="{ row }">
|
field="sid"
|
||||||
<el-tooltip class="item" effect="dark" content="查看提交详情" placement="top">
|
title="Run ID"
|
||||||
<el-button type="text" @click="showSubmitDetail(row)">{{ row.language }}</el-button>
|
min-width="100"
|
||||||
</el-tooltip>
|
></vxe-table-column>
|
||||||
</template>
|
<vxe-table-column
|
||||||
</vxe-table-column>
|
field="stime"
|
||||||
<vxe-table-column field="judger" title="Judger" width="150"></vxe-table-column>
|
title="Submit time"
|
||||||
<vxe-table-column field="author" title="Author" width="150"></vxe-table-column>
|
min-width="150"
|
||||||
</vxe-table>
|
></vxe-table-column>
|
||||||
</el-card>
|
<vxe-table-column field="pid" title="Problem ID" min-width="100">
|
||||||
<Pagination :total="total" :page-size="limit" @on-change="changeRoute" :current.sync="page"></Pagination>
|
<template v-slot="{ row }">
|
||||||
|
<a
|
||||||
|
:href="getProblemUri(row.pid)"
|
||||||
|
style="color: rgb(87, 163, 243)"
|
||||||
|
>{{ row.pid }}</a
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column field="status" title="Status" min-width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span :class="getStatusColor(row.status)">{{
|
||||||
|
JUDGE_STATUS[row.status].name
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
field="time"
|
||||||
|
title="Time"
|
||||||
|
min-width="64"
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
field="memory"
|
||||||
|
title="Memory"
|
||||||
|
min-width="96"
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column field="language" title="Language" min-width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="dark"
|
||||||
|
content="查看提交详情"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-button type="text" @click="showSubmitDetail(row)">{{
|
||||||
|
row.language
|
||||||
|
}}</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
field="judger"
|
||||||
|
title="Judger"
|
||||||
|
min-width="100"
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
field="author"
|
||||||
|
title="Author"
|
||||||
|
min-width="100"
|
||||||
|
></vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
</el-card>
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
:page-size="limit"
|
||||||
|
@on-change="changeRoute"
|
||||||
|
:current.sync="page"
|
||||||
|
></Pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
import api from '@/common/api'
|
import api from "@/common/api";
|
||||||
import { JUDGE_STATUS, USER_TYPE } from '@/common/constants'
|
import { JUDGE_STATUS, USER_TYPE } from "@/common/constants";
|
||||||
import utils from '@/common/utils'
|
import utils from "@/common/utils";
|
||||||
import time from '@/common/time'
|
import time from "@/common/time";
|
||||||
import Pagination from '@/components/common/Pagination'
|
import Pagination from "@/components/common/Pagination";
|
||||||
export default {
|
export default {
|
||||||
name: 'submissionList',
|
name: "submissionList",
|
||||||
components: {
|
components: {
|
||||||
Pagination,
|
Pagination,
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
formFilter: {
|
formFilter: {
|
||||||
myself: false,
|
myself: false,
|
||||||
status: '',
|
status: "",
|
||||||
username: '',
|
username: "",
|
||||||
pid:''
|
pid: "",
|
||||||
|
},
|
||||||
|
loadingTable: false,
|
||||||
|
submissions: [
|
||||||
|
{
|
||||||
|
sid: 1000,
|
||||||
|
stime: "2020-08-08 16:00:00",
|
||||||
|
pid: "1001",
|
||||||
|
status: 0,
|
||||||
|
time: "4ms",
|
||||||
|
memory: "3MB",
|
||||||
|
language: "C++",
|
||||||
|
judger: "192.168.1.1",
|
||||||
|
author: "Himit_ZH",
|
||||||
},
|
},
|
||||||
loadingTable: false,
|
{
|
||||||
submissions: [
|
sid: 1001,
|
||||||
{
|
stime: "2020-08-08 16:00:00",
|
||||||
sid:1000,
|
pid: "1001",
|
||||||
stime:'2020-08-08 16:00:00',
|
status: 0,
|
||||||
pid:'1001',
|
time: "1000ms",
|
||||||
status:0,
|
memory: "3MB",
|
||||||
time:'4ms',
|
language: "C++",
|
||||||
memory:'3MB',
|
judger: "192.168.1.1",
|
||||||
language:'C++',
|
author: "Himit_Z",
|
||||||
judger:'192.168.1.1',
|
},
|
||||||
author:'Himit_ZH'
|
{
|
||||||
},
|
sid: 1000,
|
||||||
{
|
stime: "2020-08-08 16:00:00",
|
||||||
sid:1001,
|
pid: "1001",
|
||||||
stime:'2020-08-08 16:00:00',
|
status: 0,
|
||||||
pid:'1001',
|
time: "4ms",
|
||||||
status:0,
|
memory: "3MB",
|
||||||
time:'4ms',
|
language: "C++",
|
||||||
memory:'3MB',
|
judger: "192.168.1.1",
|
||||||
language:'C++',
|
author: "Himit_H",
|
||||||
judger:'192.168.1.1',
|
},
|
||||||
author:'Himit_Z'
|
],
|
||||||
},
|
total: 30,
|
||||||
{
|
limit: 15,
|
||||||
sid:1000,
|
page: 1,
|
||||||
stime:'2020-08-08 16:00:00',
|
contestID: "",
|
||||||
pid:'1001',
|
problemID: "",
|
||||||
status:0,
|
routeName: "",
|
||||||
time:'4ms',
|
JUDGE_STATUS: "",
|
||||||
memory:'3MB',
|
rejudge_column: false,
|
||||||
language:'C++',
|
};
|
||||||
judger:'192.168.1.1',
|
},
|
||||||
author:'Himit_H'
|
mounted() {
|
||||||
}
|
this.init();
|
||||||
|
this.JUDGE_STATUS = Object.assign({}, JUDGE_STATUS);
|
||||||
],
|
// 去除submitting的状态 和 两个
|
||||||
total: 30,
|
delete this.JUDGE_STATUS["9"];
|
||||||
limit: 15,
|
delete this.JUDGE_STATUS["2"];
|
||||||
page: 1,
|
},
|
||||||
contestID: '',
|
methods: {
|
||||||
problemID: '',
|
init() {
|
||||||
routeName: '',
|
this.contestID = this.$route.params.contestID;
|
||||||
JUDGE_STATUS: '',
|
let query = this.$route.query;
|
||||||
rejudge_column: false
|
this.problemID = query.problemID;
|
||||||
|
this.formFilter.myself = query.myself === "1";
|
||||||
|
this.formFilter.status = query.status || "";
|
||||||
|
this.formFilter.username = query.username || "";
|
||||||
|
this.page = parseInt(query.page) || 1;
|
||||||
|
if (this.page < 1) {
|
||||||
|
this.page = 1;
|
||||||
}
|
}
|
||||||
|
this.routeName = this.$route.name;
|
||||||
|
this.getSubmissions();
|
||||||
},
|
},
|
||||||
mounted () {
|
buildQuery() {
|
||||||
this.init()
|
return {
|
||||||
this.JUDGE_STATUS = Object.assign({}, JUDGE_STATUS)
|
myself: this.formFilter.myself === true ? "1" : "0",
|
||||||
// 去除submitting的状态 和 两个
|
status: this.formFilter.status,
|
||||||
delete this.JUDGE_STATUS['9']
|
username: this.formFilter.username,
|
||||||
delete this.JUDGE_STATUS['2']
|
page: this.page,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
getSubmissions() {
|
||||||
init () {
|
let params = this.buildQuery();
|
||||||
this.contestID = this.$route.params.contestID
|
params.contest_id = this.contestID;
|
||||||
let query = this.$route.query
|
params.problem_id = this.problemID;
|
||||||
this.problemID = query.problemID
|
let offset = (this.page - 1) * this.limit;
|
||||||
this.formFilter.myself = query.myself === '1'
|
let func = this.contestID
|
||||||
this.formFilter.status = query.status || ''
|
? "getContestSubmissionList"
|
||||||
this.formFilter.username = query.username || ''
|
: "getSubmissionList";
|
||||||
this.page = parseInt(query.page) || 1
|
this.loadingTable = true;
|
||||||
if (this.page < 1) {
|
api[func](offset, this.limit, params)
|
||||||
this.page = 1
|
.then((res) => {
|
||||||
}
|
let data = res.data.data;
|
||||||
this.routeName = this.$route.name
|
|
||||||
this.getSubmissions()
|
|
||||||
},
|
|
||||||
buildQuery () {
|
|
||||||
return {
|
|
||||||
myself: this.formFilter.myself === true ? '1' : '0',
|
|
||||||
status: this.formFilter.status,
|
|
||||||
username: this.formFilter.username,
|
|
||||||
page: this.page
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getSubmissions () {
|
|
||||||
let params = this.buildQuery()
|
|
||||||
params.contest_id = this.contestID
|
|
||||||
params.problem_id = this.problemID
|
|
||||||
let offset = (this.page - 1) * this.limit
|
|
||||||
let func = this.contestID ? 'getContestSubmissionList' : 'getSubmissionList'
|
|
||||||
this.loadingTable = true
|
|
||||||
api[func](offset, this.limit, params).then(res => {
|
|
||||||
let data = res.data.data
|
|
||||||
for (let v of data.results) {
|
for (let v of data.results) {
|
||||||
v.loading = false
|
v.loading = false;
|
||||||
}
|
}
|
||||||
this.adjustRejudgeColumn()
|
this.adjustRejudgeColumn();
|
||||||
this.loadingTable = false
|
this.loadingTable = false;
|
||||||
this.submissions = data.results
|
this.submissions = data.results;
|
||||||
this.total = data.total
|
this.total = data.total;
|
||||||
}).catch(() => {
|
|
||||||
this.loadingTable = false
|
|
||||||
})
|
})
|
||||||
},
|
.catch(() => {
|
||||||
// 改变route, 通过监听route变化请求数据,这样可以产生route history, 用户返回时就会保存之前的状态
|
this.loadingTable = false;
|
||||||
changeRoute () {
|
});
|
||||||
let query = utils.filterEmptyValue(this.buildQuery())
|
},
|
||||||
query.contestID = this.contestID
|
// 改变route, 通过监听route变化请求数据,这样可以产生route history, 用户返回时就会保存之前的状态
|
||||||
query.problemID = this.problemID
|
changeRoute() {
|
||||||
let routeName = query.contestID ? 'contest-submission-list' : 'submission-list'
|
let query = utils.filterEmptyValue(this.buildQuery());
|
||||||
this.$router.push({
|
query.contestID = this.contestID;
|
||||||
name: routeName,
|
query.problemID = this.problemID;
|
||||||
query: utils.filterEmptyValue(query)
|
let routeName = query.contestID
|
||||||
})
|
? "contest-submission-list"
|
||||||
},
|
: "submission-list";
|
||||||
goRoute (route) {
|
this.$router.push({
|
||||||
this.$router.push(route)
|
name: routeName,
|
||||||
},
|
query: utils.filterEmptyValue(query),
|
||||||
adjustRejudgeColumn () {
|
});
|
||||||
if (!this.rejudgeColumnVisible || this.rejudge_column) {
|
},
|
||||||
return
|
goRoute(route) {
|
||||||
}
|
this.$router.push(route);
|
||||||
const judgeColumn = {
|
},
|
||||||
title: this.$i18n.t('m.Option'),
|
adjustRejudgeColumn() {
|
||||||
fixed: 'right',
|
if (!this.rejudgeColumnVisible || this.rejudge_column) {
|
||||||
align: 'center',
|
return;
|
||||||
width: 90,
|
}
|
||||||
render: (h, params) => {
|
const judgeColumn = {
|
||||||
return h('Button', {
|
title: this.$i18n.t("m.Option"),
|
||||||
|
fixed: "right",
|
||||||
|
align: "center",
|
||||||
|
width: 90,
|
||||||
|
render: (h, params) => {
|
||||||
|
return h(
|
||||||
|
"Button",
|
||||||
|
{
|
||||||
props: {
|
props: {
|
||||||
type: 'primary',
|
type: "primary",
|
||||||
size: 'small',
|
size: "small",
|
||||||
loading: params.row.loading
|
loading: params.row.loading,
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
click: () => {
|
click: () => {
|
||||||
this.handleRejudge(params.row.id, params.index)
|
this.handleRejudge(params.row.id, params.index);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}, this.$i18n.t('m.Rejudge'))
|
},
|
||||||
}
|
this.$i18n.t("m.Rejudge")
|
||||||
}
|
);
|
||||||
this.columns.push(judgeColumn)
|
},
|
||||||
this.rejudge_column = true
|
};
|
||||||
},
|
this.columns.push(judgeColumn);
|
||||||
handleStatusChange (status) {
|
this.rejudge_column = true;
|
||||||
this.page = 1
|
},
|
||||||
this.formFilter.status = status
|
handleStatusChange(status) {
|
||||||
this.changeRoute()
|
this.page = 1;
|
||||||
},
|
this.formFilter.status = status;
|
||||||
handleQueryChange () {
|
this.changeRoute();
|
||||||
this.page = 1
|
},
|
||||||
this.changeRoute()
|
handleQueryChange() {
|
||||||
},
|
this.page = 1;
|
||||||
handleRejudge (id, index) {
|
this.changeRoute();
|
||||||
this.submissions[index].loading = true
|
},
|
||||||
api.submissionRejudge(id).then(res => {
|
handleRejudge(id, index) {
|
||||||
this.submissions[index].loading = false
|
this.submissions[index].loading = true;
|
||||||
this.$success('Succeeded')
|
api.submissionRejudge(id).then(
|
||||||
this.getSubmissions()
|
(res) => {
|
||||||
}, () => {
|
this.submissions[index].loading = false;
|
||||||
this.submissions[index].loading = false
|
this.$success("Succeeded");
|
||||||
})
|
this.getSubmissions();
|
||||||
},
|
},
|
||||||
showSubmitDetail (row) {
|
() => {
|
||||||
this.selectSubmitRow = row;
|
this.submissions[index].loading = false;
|
||||||
this.showDetails = true;
|
|
||||||
},
|
|
||||||
getProblemUri(pid){
|
|
||||||
return '/problem/'+pid;
|
|
||||||
},
|
|
||||||
getStatusColor(status){
|
|
||||||
return 'el-tag el-tag--medium status-'+JUDGE_STATUS[status].color;
|
|
||||||
},
|
|
||||||
tableRowClassName({row, rowIndex}){
|
|
||||||
if(row.author =='Himit_ZH'){
|
|
||||||
return 'own-submit-row';
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showSubmitDetail(row) {
|
||||||
|
this.selectSubmitRow = row;
|
||||||
|
this.showDetails = true;
|
||||||
|
},
|
||||||
|
getProblemUri(pid) {
|
||||||
|
return "/problem/" + pid;
|
||||||
|
},
|
||||||
|
getStatusColor(status) {
|
||||||
|
return "el-tag el-tag--medium status-" + JUDGE_STATUS[status].color;
|
||||||
|
},
|
||||||
|
tableRowClassName({ row, rowIndex }) {
|
||||||
|
if (row.author == "Himit_ZH") {
|
||||||
|
return "own-submit-row";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
},
|
||||||
...mapGetters(['isAuthenticated', 'userInfo']),
|
computed: {
|
||||||
title () {
|
...mapGetters(["isAuthenticated", "userInfo"]),
|
||||||
if (!this.contestID) {
|
title() {
|
||||||
return 'Status'
|
if (!this.contestID) {
|
||||||
} else if (this.problemID) {
|
return "Status";
|
||||||
return 'Problem Submissions'
|
} else if (this.problemID) {
|
||||||
} else {
|
return "Problem Submissions";
|
||||||
return 'Submissions'
|
} else {
|
||||||
}
|
return "Submissions";
|
||||||
},
|
|
||||||
status () {
|
|
||||||
return this.formFilter.status === '' ? 'Status' : JUDGE_STATUS[this.formFilter.status].name.replace(/ /g, '_')
|
|
||||||
},
|
|
||||||
rejudgeColumnVisible () {
|
|
||||||
return !this.contestID && this.userInfo.role === USER_TYPE.SUPER_ADMIN
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
status() {
|
||||||
'$route' (newVal, oldVal) {
|
return this.formFilter.status === ""
|
||||||
if (newVal !== oldVal) {
|
? "Status"
|
||||||
this.init()
|
: JUDGE_STATUS[this.formFilter.status].name.replace(/ /g, "_");
|
||||||
}
|
},
|
||||||
},
|
rejudgeColumnVisible() {
|
||||||
'rejudgeColumnVisible' () {
|
return !this.contestID && this.userInfo.role === USER_TYPE.SUPER_ADMIN;
|
||||||
this.adjustRejudgeColumn()
|
},
|
||||||
},
|
},
|
||||||
'isAuthenticated' () {
|
watch: {
|
||||||
this.init()
|
$route(newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
rejudgeColumnVisible() {
|
||||||
|
this.adjustRejudgeColumn();
|
||||||
|
},
|
||||||
|
isAuthenticated() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped >
|
<style scoped >
|
||||||
|
@media only screen and (max-width: 767px) {
|
||||||
|
.search {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 767px){
|
.flex-container #main {
|
||||||
.search{
|
flex: auto;
|
||||||
margin-top: 20px;
|
}
|
||||||
}
|
.flex-container .filter {
|
||||||
}
|
margin-right: -10px;
|
||||||
|
}
|
||||||
.flex-container #main {
|
.flex-container #contest-menu {
|
||||||
flex: auto;
|
flex: none;
|
||||||
margin-right: 18px;
|
width: 210px;
|
||||||
}
|
}
|
||||||
.flex-container .filter {
|
/deep/ .el-card__header {
|
||||||
margin-right: -10px;
|
border-bottom: 0px;
|
||||||
}
|
padding-bottom: 0px;
|
||||||
.flex-container #contest-menu {
|
text-align: center;
|
||||||
flex: none;
|
}
|
||||||
width: 210px;
|
/deep/ .el-button {
|
||||||
}
|
padding: 0;
|
||||||
/deep/ .el-card__header {
|
}
|
||||||
border-bottom: 0px;
|
/deep/ .el-dialog {
|
||||||
padding-bottom: 0px;
|
border-radius: 6px !important;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
/deep/ .el-button{
|
/deep/ .el-switch {
|
||||||
padding: 0;
|
padding-top: 6px;
|
||||||
}
|
}
|
||||||
/deep/ .el-dialog {
|
|
||||||
border-radius: 6px !important;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
/deep/ .el-switch{
|
|
||||||
padding-top: 6px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue