前端页面添加
This commit is contained in:
parent
b31b8a9b3c
commit
98f0c2da2f
|
@ -13,6 +13,7 @@
|
|||
| 2020-10-30 | 评测模块接口,判题服务系统,初始化前端vue项目 | Himit_ZH |
|
||||
| 2020-11-08 | 前端vue主页,题目列表页,登录,注册,重置密码弹窗逻辑 | Himit_ZH |
|
||||
| 2020-11-16 | 前端提交列表页,提交详情页,题目详情页,排行(ACM,OI)页,比赛列表页,个人主页,个人设置页 | Himit_ZH |
|
||||
| 2020-11-22 | 前端比赛首页,比赛题目列表,比赛排行榜,比赛公告,首页布局调整 | Himit_ZH |
|
||||
|
||||
# 二、系统架构
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<NavBar></NavBar>
|
||||
<div id="app">
|
||||
<transition name="fadeInUp" mode="out-in">
|
||||
<transition name="el-zoom-in-bottom">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
</div>
|
||||
|
@ -24,6 +24,7 @@ export default {
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
body{
|
||||
background-color: #eee;
|
||||
|
@ -35,7 +36,7 @@ export default {
|
|||
code, kbd, pre, samp {
|
||||
font-family: Consolas,Menlo,Courier,monospace;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
|
@ -57,6 +58,27 @@ export default {
|
|||
padding-bottom: 20px;
|
||||
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{
|
||||
background-color: #19be6b!important;
|
||||
color: #fff!important;
|
||||
|
@ -83,6 +105,10 @@ export default {
|
|||
.vxe-table{
|
||||
color: #495060!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 {
|
||||
background: #66B1FF !important;
|
||||
|
@ -99,10 +125,22 @@ export default {
|
|||
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) {
|
||||
#app {
|
||||
margin-top: 270px;
|
||||
padding: 0 4%;
|
||||
padding: 0 0;
|
||||
}
|
||||
.markdown-body img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#problem-content .sample pre {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import axios from 'axios'
|
||||
import mMessage from '@/common/message'
|
||||
import NProgress from 'nprogress' // nprogress插件
|
||||
import 'nprogress/nprogress.css' // nprogress样式
|
||||
// import NProgress from 'nprogress' // nprogress插件
|
||||
// import 'nprogress/nprogress.css' // nprogress样式
|
||||
|
||||
// 配置NProgress进度条选项 —— 动画效果
|
||||
NProgress.configure({ ease: 'ease', speed: 1000,showSpinner: false})
|
||||
// // 配置NProgress进度条选项 —— 动画效果
|
||||
// NProgress.configure({ ease: 'ease', speed: 1000,showSpinner: false})
|
||||
|
||||
// 环境的切换
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
|
@ -22,7 +22,7 @@ axios.interceptors.request.use(
|
|||
|
||||
config => {
|
||||
|
||||
NProgress.start();
|
||||
// NProgress.start();
|
||||
// 每次发送请求之前判断vuex中是否存在token
|
||||
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
|
||||
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
|
||||
|
@ -32,7 +32,7 @@ axios.interceptors.request.use(
|
|||
return config;
|
||||
},
|
||||
error => {
|
||||
NProgress.done();
|
||||
// NProgress.done();
|
||||
mmMessage.error(error.response.data.mMessage);
|
||||
return Promise.error(error);
|
||||
})
|
||||
|
@ -40,7 +40,7 @@ axios.interceptors.request.use(
|
|||
// 响应拦截器
|
||||
axios.interceptors.response.use(
|
||||
response => {
|
||||
NProgress.done();
|
||||
// NProgress.done();
|
||||
if (response.data.status === 200) {
|
||||
return Promise.resolve(response);
|
||||
} else {
|
||||
|
@ -50,7 +50,7 @@ axios.interceptors.response.use(
|
|||
},
|
||||
// 服务器状态码不是200的情况
|
||||
error => {
|
||||
NProgress.done();
|
||||
// NProgress.done();
|
||||
if (error.response) {
|
||||
switch (error.response.status) {
|
||||
// 401: 未登录
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
export const JUDGE_STATUS = {
|
||||
'-10': {
|
||||
name: 'Not submitted',
|
||||
short: 'NS',
|
||||
color: 'gray',
|
||||
type: 'info',
|
||||
rgb:'#909399'
|
||||
},
|
||||
'-3': {
|
||||
name: 'Presentation Error',
|
||||
short: 'PE',
|
||||
|
@ -156,7 +163,8 @@ export const CONTEST_TYPE_REVERSE = {
|
|||
|
||||
export const CONTEST_TYPE = {
|
||||
PUBLIC: 'Public',
|
||||
PRIVATE: 'Password Protected'
|
||||
PRIVATE: 'Password Protected',
|
||||
PROTECT: 'Submit Protected'
|
||||
}
|
||||
|
||||
export const USER_TYPE = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import moment from 'moment'
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,22 @@ function duration (startTime, endTime) {
|
|||
return Math.abs(duration.asHours().toFixed(1)) + ' hours'
|
||||
}
|
||||
|
||||
function secondFormat (seconds) {
|
||||
let m = moment.duration(seconds, 'seconds')
|
||||
return Math.floor(m.asHours()) + ':' + m.minutes() + ':' + m.seconds()
|
||||
function secondFormat (time) {
|
||||
let m = moment.duration(time, '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 {
|
||||
utcToLocal: utcToLocal,
|
||||
duration: duration,
|
||||
secondFormat: secondFormat
|
||||
secondFormat: secondFormat,
|
||||
durationMs:durationMs,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import storage from '@/common/storage'
|
||||
import { STORAGE_KEY } from '@/common/constants'
|
||||
import ojAPI from '@/common/api'
|
||||
|
||||
function submissionMemoryFormat (memory) {
|
||||
if (memory === undefined) return '--'
|
||||
|
@ -39,7 +38,7 @@ function breakLongWords (value, length = 16) {
|
|||
re = new RegExp('(.{' + length + '})', 'g')
|
||||
} else {
|
||||
// 中文字符
|
||||
re = new RegExp('(.{' + (length / 2 + 1) + '})', 'g')
|
||||
re = new RegExp('(.{' + (parseInt(length / 2) + 1) + '})', 'g')
|
||||
}
|
||||
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 ACMRank from "@/views/rank/ACMRank.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 = [
|
||||
{
|
||||
path: '/count-down',
|
||||
name: 'CountDown',
|
||||
component: CountDown
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
redirect: {
|
||||
|
@ -86,5 +84,43 @@ const routes = [
|
|||
component: 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
|
||||
|
|
|
@ -138,8 +138,8 @@ const actions = {
|
|||
api.getContest(rootState.route.params.contestID).then((res) => {
|
||||
resolve(res)
|
||||
let contest = res.data.data
|
||||
commit(types.CHANGE_CONTEST, {contest: contest})
|
||||
commit(types.NOW, {now: moment(contest.now)})
|
||||
commit('changeContest', {contest: contest})
|
||||
commit('now', {now: moment(contest.now)})
|
||||
if (contest.contest_type === CONTEST_TYPE.PRIVATE) {
|
||||
dispatch('getContestAccess')
|
||||
}
|
||||
|
@ -159,17 +159,17 @@ const actions = {
|
|||
}
|
||||
return -1
|
||||
})
|
||||
commit(types.CHANGE_CONTEST_PROBLEMS, {contestProblems: res.data.data})
|
||||
commit('changeContestProblems', {contestProblems: res.data.data})
|
||||
resolve(res)
|
||||
}, () => {
|
||||
commit(types.CHANGE_CONTEST_PROBLEMS, {contestProblems: []})
|
||||
commit('changeContestProblems', {contestProblems: []})
|
||||
})
|
||||
})
|
||||
},
|
||||
getContestAccess ({commit, rootState}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
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)
|
||||
}).catch()
|
||||
})
|
||||
|
|
|
@ -76,9 +76,9 @@ const rootActions = {
|
|||
},
|
||||
changeDomTitle ({commit, state}, payload) {
|
||||
if (payload && payload.title) {
|
||||
window.document.title = state.website.website_name_shortcut + ' | ' + payload.title
|
||||
window.document.title = 'HOJ' + ' - ' + payload.title
|
||||
} 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>
|
||||
<div>
|
||||
<el-card class="contest">
|
||||
<div slot="header" class="clearfix title">
|
||||
<el-link @click="goContest" :underline="false">比赛标题</el-link>
|
||||
</div>
|
||||
<el-carousel indicator-position="outside" height="200px" :interval='interval'>
|
||||
<el-carousel-item v-for="(contest,index) in contests" :key="index">
|
||||
<div class="contest-info">
|
||||
<el-card class="contest">
|
||||
<div slot="header" class="clearfix title">
|
||||
<el-link @click="goContest" :underline="false">比赛标题</el-link>
|
||||
</div>
|
||||
<el-carousel
|
||||
indicator-position="outside"
|
||||
height="200px"
|
||||
:interval="interval"
|
||||
>
|
||||
<el-carousel-item v-for="(contest, index) in contests" :key="index">
|
||||
<div class="contest-info">
|
||||
<div class="contest-tags">
|
||||
<el-button type="primary" round size="mini"><i class="fa fa-calendar"></i>
|
||||
{{contest.beginTime | localtime }}
|
||||
<el-button type="primary" round size="mini"
|
||||
><i class="fa fa-calendar"></i>
|
||||
{{ contest.beginTime | localtime }}
|
||||
</el-button>
|
||||
<el-button type="success" round size="mini"><i class="fa fa-clock-o"></i>
|
||||
{{contest.duration}}
|
||||
<el-button type="success" round size="mini"
|
||||
><i class="fa fa-clock-o"></i>
|
||||
{{ contest.duration }}
|
||||
</el-button>
|
||||
<el-button type="warning" round size="mini"><i class="fa fa-trophy"></i>
|
||||
{{contest.type}}
|
||||
<el-button type="warning" round size="mini"
|
||||
><i class="fa fa-trophy"></i>
|
||||
{{ contest.type }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="contest-description">
|
||||
<blockquote v-html="contest.description"></blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
<el-row>
|
||||
<el-col :md="14" :sm="24">
|
||||
<el-card class="box-card">
|
||||
<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>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="12" :sm="24">
|
||||
<Announcements></Announcements>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import time from "@/common/time";
|
||||
import Announcements from "@/components/common/Announcements.vue";
|
||||
export default {
|
||||
name:"home",
|
||||
data() {
|
||||
return {
|
||||
interval:6000,
|
||||
tableData: [{
|
||||
oj: 'Codeforces',
|
||||
title: 'Codeforces Round #680 (Div. 1, based on VK Cup 2020-2021 - Final)',
|
||||
beginTime: '2020-11-08T05:00:00Z',
|
||||
endTime:'2020-11-08T08:00:00Z',
|
||||
},],
|
||||
contests: [
|
||||
{
|
||||
beginTime:'2020-11-08T05:00:00Z',
|
||||
duration:'5hours',
|
||||
type:"ACM",
|
||||
description:'<h1>描述</h1>',
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
goContest(){
|
||||
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
name: "home",
|
||||
components: {
|
||||
Announcements,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: 6000,
|
||||
otherContests: [
|
||||
{
|
||||
oj: "Codeforces",
|
||||
title:
|
||||
"Codeforces Round #680 (Div. 1, based on VK Cup 2020-2021 - Final)",
|
||||
beginTime: "2020-11-08T05:00:00Z",
|
||||
endTime: "2020-11-08T08:00:00Z",
|
||||
},
|
||||
],
|
||||
contests: [
|
||||
{
|
||||
beginTime: "2020-11-08T05:00:00Z",
|
||||
duration: "5hours",
|
||||
type: "ACM",
|
||||
description: "<h1>描述</h1>",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
goContest() {},
|
||||
},
|
||||
filters: {
|
||||
localtime(value) {
|
||||
return time.utcToLocal(value);
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-carousel__item h3 {
|
||||
|
@ -134,37 +119,37 @@ export default {
|
|||
background-color: #fff;
|
||||
}
|
||||
|
||||
.el-col{
|
||||
.el-col {
|
||||
margin-top: 25px;
|
||||
}
|
||||
.contest{
|
||||
.contest {
|
||||
text-align: center;
|
||||
}
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.contest .title .el-link {
|
||||
font-size: 25px;
|
||||
font-weight: 500;
|
||||
color: #444;
|
||||
}
|
||||
.clearfix h2{
|
||||
color: #409EFF;
|
||||
.clearfix h2 {
|
||||
color: #409eff;
|
||||
}
|
||||
.el-link.el-link--default:hover {
|
||||
color: #409EFF;
|
||||
transition: all 0.28s ease;
|
||||
color: #409eff;
|
||||
transition: all 0.28s ease;
|
||||
}
|
||||
.contest .content-info {
|
||||
.contest .content-info {
|
||||
padding: 0 70px 40px 70px;
|
||||
}
|
||||
.contest .contest-description{
|
||||
.contest .contest-description {
|
||||
margin-top: 25px;
|
||||
}
|
||||
</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
|
||||
border="inner"
|
||||
stripe
|
||||
auto-resize
|
||||
@cell-mouseenter="cellHover"
|
||||
: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 }">
|
||||
<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>
|
||||
</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 }">
|
||||
<span :class="getLevelColor(row.level)">{{PROBLEM_LEVEL[row.level].name}}</span>
|
||||
</template>
|
||||
</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 }">
|
||||
<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>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="total" title="Total" width="100"></vxe-table-column>
|
||||
<vxe-table-column field="ACRate" title="AC Rate" 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" min-width="80"></vxe-table-column>
|
||||
</vxe-table>
|
||||
</el-card>
|
||||
<Pagination :total="total" :page-size="limit" @on-change="pushRouter" :current.sync="query.page"></Pagination>
|
||||
|
@ -117,25 +126,25 @@
|
|||
name:'模拟题'
|
||||
}],
|
||||
problemRecord:[
|
||||
{status:'0',count:'70'},
|
||||
{status:'-1',count:'70'},
|
||||
{status:'3',count:'70'},
|
||||
{status:'1',count:'70'},
|
||||
{status:'4',count:'70'},
|
||||
{status:'-3',count:'70'},
|
||||
{status:'-2',count:'70'},
|
||||
{status:'5',count:'70'},
|
||||
{status:'0',count:70},
|
||||
{status:'-1',count:70},
|
||||
{status:'3',count:70},
|
||||
{status:'1',count:70},
|
||||
{status:'4',count:70},
|
||||
{status:'-3',count:70},
|
||||
{status:'-2',count:70},
|
||||
{status:'5',count:70},
|
||||
],
|
||||
problemList: [
|
||||
{pid:'1000',title:'测试标题',level:0,
|
||||
{myStatus:-10,pid:'1000',title:'测试标题',level:0,
|
||||
tags:['简单题','模拟题'],
|
||||
total:'10000',ACRate:'59.12%'
|
||||
},
|
||||
{pid:'1000',title:'测试标题',level:1,
|
||||
tags:['简单题','模拟题'],
|
||||
{myStatus:-1,pid:'1000',title:'测试标题',level:1,
|
||||
tags:['简单题','模拟题','递归题','递归题'],
|
||||
total:'10000',ACRate:'59.12%'
|
||||
},
|
||||
{pid:'1000',title:'测试标题',level:2,
|
||||
{myStatus:0,pid:'1000',title:'测试标题',level:2,
|
||||
tags:['简单题','模拟题'],
|
||||
total:'10000',ACRate:'59.12%'
|
||||
},
|
||||
|
@ -239,6 +248,9 @@
|
|||
},
|
||||
getLevelColor(level){
|
||||
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: {
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="space-around">
|
||||
<el-col :span="22">
|
||||
<el-col :span="24">
|
||||
<el-card :padding="10">
|
||||
<div slot="header"><span class="panel-title">ACM Ranklist</span></div>
|
||||
<div class="echarts">
|
||||
<ECharts :options="options" ref="chart" auto-resize></ECharts>
|
||||
<ECharts :options="options" ref="chart" :autoresize="true"></ECharts>
|
||||
</div>
|
||||
</el-card>
|
||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row style="font-weight: 500;">
|
||||
<vxe-table-column type="seq" width="100"></vxe-table-column>
|
||||
<vxe-table-column field="username" title="User" width="197">
|
||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row auto-resize style="font-weight: 500;">
|
||||
<vxe-table-column type="seq" min-width="50"></vxe-table-column>
|
||||
<vxe-table-column field="username" title="User" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="nickname" title="Nickname" width="197"></vxe-table-column>
|
||||
<vxe-table-column field="signature" title="Mood" width="197"></vxe-table-column>
|
||||
<vxe-table-column field="ac" title="AC" width="197"></vxe-table-column>
|
||||
<vxe-table-column field="total" title="Total" width="197"></vxe-table-column>
|
||||
<vxe-table-column field="rating" title="Rating" 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" min-width="180"></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="rating" title="Rating" min-width="80"></vxe-table-column>
|
||||
</vxe-table>
|
||||
|
||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||
|
@ -45,12 +45,7 @@
|
|||
limit: 30,
|
||||
total: 0,
|
||||
loadingTable: false,
|
||||
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%'},
|
||||
|
||||
],
|
||||
dataRank: [],
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
|
@ -60,7 +55,9 @@
|
|||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%'
|
||||
x2: '3%',
|
||||
left:'8%',
|
||||
right:'8%'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
|
@ -69,7 +66,7 @@
|
|||
magicType: {show: true, type: ['line', 'bar', 'stack']},
|
||||
saveAsImage: {show: true}
|
||||
},
|
||||
right: '5%',
|
||||
right: '8%',
|
||||
top:'5%'
|
||||
},
|
||||
calculable: true,
|
||||
|
@ -83,14 +80,20 @@
|
|||
showMaxLabel: true,
|
||||
align: 'center',
|
||||
formatter: (value, index) => {
|
||||
return utils.breakLongWords(value, 10)
|
||||
return utils.breakLongWords(value, 13)
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate:50,
|
||||
textStyle:{
|
||||
fontSize:'12em',
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
|
@ -120,6 +123,21 @@
|
|||
},
|
||||
mounted () {
|
||||
// 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: {
|
||||
getRankData (page) {
|
||||
|
@ -143,9 +161,9 @@
|
|||
changeCharts (rankData) {
|
||||
let [usernames, acData, totalData] = [[], [], []]
|
||||
rankData.forEach(ele => {
|
||||
usernames.push(ele.user.username)
|
||||
acData.push(ele.accepted_number)
|
||||
totalData.push(ele.submission_number)
|
||||
usernames.push(ele.username)
|
||||
acData.push(ele.ac)
|
||||
totalData.push(ele.total)
|
||||
})
|
||||
this.options.xAxis[0].data = usernames
|
||||
this.options.series[0].data = acData
|
||||
|
@ -154,6 +172,15 @@
|
|||
getInfoByUsername(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>
|
||||
|
@ -161,7 +188,12 @@
|
|||
<style scoped>
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 95%;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="space-around">
|
||||
<el-col :span="22">
|
||||
<el-col :span="24">
|
||||
<el-card :padding="10">
|
||||
<div slot="header"><span class="panel-title">OI Ranklist</span></div>
|
||||
<div class="echarts">
|
||||
<ECharts :options="options" ref="chart" auto-resize></ECharts>
|
||||
</div>
|
||||
</el-card>
|
||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row style="font-weight: 500;">
|
||||
<vxe-table-column type="seq" width="100"></vxe-table-column>
|
||||
<vxe-table-column field="username" title="User" width="168">
|
||||
<vxe-table :data="dataRank" :loading="loadingTable" align="center" highlight-hover-row auto-resize style="font-weight: 500;">
|
||||
<vxe-table-column type="seq" min-width="50"></vxe-table-column>
|
||||
<vxe-table-column field="username" title="User" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<a :href="getInfoByUsername(row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="nickname" title="Nickname" width="168"></vxe-table-column>
|
||||
<vxe-table-column field="signature" title="Mood" width="173"></vxe-table-column>
|
||||
<vxe-table-column field="score" title="Score" width="168"></vxe-table-column>
|
||||
<vxe-table-column field="ac" title="AC" width="168"></vxe-table-column>
|
||||
<vxe-table-column field="total" title="Total" width="168"></vxe-table-column>
|
||||
<vxe-table-column field="rating" title="Rating" 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" min-width="180"></vxe-table-column>
|
||||
<vxe-table-column field="score" title="Score" min-width="80"></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="rating" title="Rating" min-width="80"></vxe-table-column>
|
||||
</vxe-table>
|
||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||
@on-change="getRankData"
|
||||
|
@ -44,11 +44,7 @@
|
|||
page: 1,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
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%'},
|
||||
],
|
||||
dataRank:[],
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
|
@ -58,7 +54,9 @@
|
|||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%'
|
||||
x2: '3%',
|
||||
left:'8%',
|
||||
right:'8%'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
|
@ -67,8 +65,8 @@
|
|||
magicType: {show: true, type: ['line', 'bar']},
|
||||
saveAsImage: {show: true}
|
||||
},
|
||||
right: '5%',
|
||||
top:'5%'
|
||||
right: '8%',
|
||||
top:'5%',
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
|
@ -92,7 +90,13 @@
|
|||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate:50,
|
||||
textStyle:{
|
||||
fontSize:'12em',
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
|
@ -113,6 +117,20 @@
|
|||
},
|
||||
mounted () {
|
||||
// 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: {
|
||||
getRankData (page) {
|
||||
|
@ -131,8 +149,8 @@
|
|||
changeCharts (rankData) {
|
||||
let [usernames, scores] = [[], []]
|
||||
rankData.forEach(ele => {
|
||||
usernames.push(ele.user.username)
|
||||
scores.push(ele.total_score)
|
||||
usernames.push(ele.username)
|
||||
scores.push(ele.score)
|
||||
})
|
||||
this.options.xAxis[0].data = usernames
|
||||
this.options.series[0].data = scores
|
||||
|
@ -147,7 +165,12 @@
|
|||
<style scoped>
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 95%;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
</el-col>
|
||||
|
||||
<el-col v-if="submission.data && !isCE" :span="22">
|
||||
<vxe-table align="center" :data="submission.data" stripe style="padding-top: 13px;">
|
||||
<vxe-table-column field="sid" title="ID" width="213"></vxe-table-column>
|
||||
<vxe-table align="center" :data="submission.data" stripe auto-resize style="padding-top: 13px;">
|
||||
<vxe-table-column field="sid" title="ID" min-width="100"></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="stime"
|
||||
title="Submit time"
|
||||
width="213"
|
||||
min-width="150"
|
||||
></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 }">
|
||||
<a
|
||||
:href="getProblemUri(row.pid)"
|
||||
|
@ -37,7 +37,7 @@
|
|||
>
|
||||
</template>
|
||||
</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 }">
|
||||
<span :class="getStatusColor(row.status)">{{
|
||||
JUDGE_STATUS[row.status].name
|
||||
|
@ -47,12 +47,12 @@
|
|||
<vxe-table-column
|
||||
field="time"
|
||||
title="Time"
|
||||
width="213"
|
||||
min-width="64"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="memory"
|
||||
title="Memory"
|
||||
width="213"
|
||||
min-width="96"
|
||||
></vxe-table-column>
|
||||
</vxe-table>
|
||||
</el-col>
|
||||
|
@ -120,8 +120,8 @@ export default {
|
|||
stime: "2020-08-08 16:00:00",
|
||||
pid: "1001",
|
||||
status: 0,
|
||||
time: "4ms",
|
||||
memory: "3MB",
|
||||
time: "1000ms",
|
||||
memory: "9999MB",
|
||||
language:'Java',
|
||||
author: "Himit_ZH",
|
||||
err_info:'CE错误',
|
||||
|
|
|
@ -2,348 +2,421 @@
|
|||
<div class="flex-container">
|
||||
<div id="main">
|
||||
<el-card shadow>
|
||||
<div slot="header">
|
||||
<el-row :gutter="18">
|
||||
<el-col :md="2" :lg="2">
|
||||
<span class="panel-title hidden-xs-only" >Status</span>
|
||||
</el-col>
|
||||
<div slot="header">
|
||||
<el-row :gutter="18">
|
||||
<el-col :md="2" :lg="2">
|
||||
<span class="panel-title hidden-xs-only">Status</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="16" :md="10" :lg="10">
|
||||
<el-switch
|
||||
style="display: block"
|
||||
v-model="formFilter.myself"
|
||||
active-color="#ff4949"
|
||||
active-text="Mine"
|
||||
:width="40"
|
||||
inactive-text="All">
|
||||
</el-switch>
|
||||
</el-col>
|
||||
<el-col :xs="16" :md="10" :lg="10">
|
||||
<el-switch
|
||||
style="display: block"
|
||||
v-model="formFilter.myself"
|
||||
active-color="#ff4949"
|
||||
active-text="Mine"
|
||||
:width="40"
|
||||
inactive-text="All"
|
||||
>
|
||||
</el-switch>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="8" :md="4" :lg="4">
|
||||
<el-dropdown
|
||||
class="drop-menu"
|
||||
@command="handleStatusChange"
|
||||
placement="bottom"
|
||||
trigger="hover"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
{{formFilter.status === '' ? 'Status' : formFilter.status}}
|
||||
<i class="el-icon-caret-bottom"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="All">All</el-dropdown-item>
|
||||
<el-dropdown-item v-for="result in Object.keys(JUDGE_STATUS)" :key="result" :command="result">
|
||||
{{JUDGE_STATUS[result].name}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<el-col :xs="8" :md="4" :lg="4">
|
||||
<el-dropdown
|
||||
class="drop-menu"
|
||||
@command="handleStatusChange"
|
||||
placement="bottom"
|
||||
trigger="hover"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
{{ formFilter.status === "" ? "Status" : formFilter.status }}
|
||||
<i class="el-icon-caret-bottom"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="All">All</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-for="result in Object.keys(JUDGE_STATUS)"
|
||||
:key="result"
|
||||
:command="result"
|
||||
>
|
||||
{{ JUDGE_STATUS[result].name }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
|
||||
<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>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="4" :lg="4" class="search">
|
||||
<vxe-input v-model="formFilter.pid" placeholder="Enter Author" type="search" size="medium" @search-click="filterByKeyword"></vxe-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<vxe-table
|
||||
border="inner"
|
||||
stripe
|
||||
highlight-current-row
|
||||
highlight-hover-row
|
||||
align="center"
|
||||
:row-class-name="tableRowClassName"
|
||||
:data="submissions">
|
||||
<vxe-table-column field="sid" title="ID" width="150"></vxe-table-column>
|
||||
<vxe-table-column field="stime" title="Submit time" width="150"></vxe-table-column>
|
||||
<vxe-table-column field="pid" title="Problem ID" width="150">
|
||||
<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" width="150">
|
||||
<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" width="150"></vxe-table-column>
|
||||
<vxe-table-column field="memory" title="Memory" width="150"></vxe-table-column>
|
||||
<vxe-table-column field="language" title="Language" width="150">
|
||||
<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" width="150"></vxe-table-column>
|
||||
<vxe-table-column field="author" title="Author" width="150"></vxe-table-column>
|
||||
</vxe-table>
|
||||
</el-card>
|
||||
<Pagination :total="total" :page-size="limit" @on-change="changeRoute" :current.sync="page"></Pagination>
|
||||
<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>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="4" :lg="4" class="search">
|
||||
<vxe-input
|
||||
v-model="formFilter.pid"
|
||||
placeholder="Enter Author"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
></vxe-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<vxe-table
|
||||
border="inner"
|
||||
stripe
|
||||
highlight-current-row
|
||||
highlight-hover-row
|
||||
align="center"
|
||||
auto-resize
|
||||
:row-class-name="tableRowClassName"
|
||||
:data="submissions"
|
||||
>
|
||||
<vxe-table-column
|
||||
field="sid"
|
||||
title="Run ID"
|
||||
min-width="100"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="stime"
|
||||
title="Submit time"
|
||||
min-width="150"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column field="pid" title="Problem ID" min-width="100">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import api from '@/common/api'
|
||||
import { JUDGE_STATUS, USER_TYPE } from '@/common/constants'
|
||||
import utils from '@/common/utils'
|
||||
import time from '@/common/time'
|
||||
import Pagination from '@/components/common/Pagination'
|
||||
export default {
|
||||
name: 'submissionList',
|
||||
components: {
|
||||
Pagination,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
formFilter: {
|
||||
myself: false,
|
||||
status: '',
|
||||
username: '',
|
||||
pid:''
|
||||
import { mapGetters } from "vuex";
|
||||
import api from "@/common/api";
|
||||
import { JUDGE_STATUS, USER_TYPE } from "@/common/constants";
|
||||
import utils from "@/common/utils";
|
||||
import time from "@/common/time";
|
||||
import Pagination from "@/components/common/Pagination";
|
||||
export default {
|
||||
name: "submissionList",
|
||||
components: {
|
||||
Pagination,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formFilter: {
|
||||
myself: false,
|
||||
status: "",
|
||||
username: "",
|
||||
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: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'
|
||||
},
|
||||
{
|
||||
sid:1001,
|
||||
stime:'2020-08-08 16:00:00',
|
||||
pid:'1001',
|
||||
status:0,
|
||||
time:'4ms',
|
||||
memory:'3MB',
|
||||
language:'C++',
|
||||
judger:'192.168.1.1',
|
||||
author:'Himit_Z'
|
||||
},
|
||||
{
|
||||
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_H'
|
||||
}
|
||||
|
||||
],
|
||||
total: 30,
|
||||
limit: 15,
|
||||
page: 1,
|
||||
contestID: '',
|
||||
problemID: '',
|
||||
routeName: '',
|
||||
JUDGE_STATUS: '',
|
||||
rejudge_column: false
|
||||
{
|
||||
sid: 1001,
|
||||
stime: "2020-08-08 16:00:00",
|
||||
pid: "1001",
|
||||
status: 0,
|
||||
time: "1000ms",
|
||||
memory: "3MB",
|
||||
language: "C++",
|
||||
judger: "192.168.1.1",
|
||||
author: "Himit_Z",
|
||||
},
|
||||
{
|
||||
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_H",
|
||||
},
|
||||
],
|
||||
total: 30,
|
||||
limit: 15,
|
||||
page: 1,
|
||||
contestID: "",
|
||||
problemID: "",
|
||||
routeName: "",
|
||||
JUDGE_STATUS: "",
|
||||
rejudge_column: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
this.JUDGE_STATUS = Object.assign({}, JUDGE_STATUS);
|
||||
// 去除submitting的状态 和 两个
|
||||
delete this.JUDGE_STATUS["9"];
|
||||
delete this.JUDGE_STATUS["2"];
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.contestID = this.$route.params.contestID;
|
||||
let query = this.$route.query;
|
||||
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 () {
|
||||
this.init()
|
||||
this.JUDGE_STATUS = Object.assign({}, JUDGE_STATUS)
|
||||
// 去除submitting的状态 和 两个
|
||||
delete this.JUDGE_STATUS['9']
|
||||
delete this.JUDGE_STATUS['2']
|
||||
buildQuery() {
|
||||
return {
|
||||
myself: this.formFilter.myself === true ? "1" : "0",
|
||||
status: this.formFilter.status,
|
||||
username: this.formFilter.username,
|
||||
page: this.page,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
this.contestID = this.$route.params.contestID
|
||||
let query = this.$route.query
|
||||
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()
|
||||
},
|
||||
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
|
||||
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) {
|
||||
v.loading = false
|
||||
v.loading = false;
|
||||
}
|
||||
this.adjustRejudgeColumn()
|
||||
this.loadingTable = false
|
||||
this.submissions = data.results
|
||||
this.total = data.total
|
||||
}).catch(() => {
|
||||
this.loadingTable = false
|
||||
this.adjustRejudgeColumn();
|
||||
this.loadingTable = false;
|
||||
this.submissions = data.results;
|
||||
this.total = data.total;
|
||||
})
|
||||
},
|
||||
// 改变route, 通过监听route变化请求数据,这样可以产生route history, 用户返回时就会保存之前的状态
|
||||
changeRoute () {
|
||||
let query = utils.filterEmptyValue(this.buildQuery())
|
||||
query.contestID = this.contestID
|
||||
query.problemID = this.problemID
|
||||
let routeName = query.contestID ? 'contest-submission-list' : 'submission-list'
|
||||
this.$router.push({
|
||||
name: routeName,
|
||||
query: utils.filterEmptyValue(query)
|
||||
})
|
||||
},
|
||||
goRoute (route) {
|
||||
this.$router.push(route)
|
||||
},
|
||||
adjustRejudgeColumn () {
|
||||
if (!this.rejudgeColumnVisible || this.rejudge_column) {
|
||||
return
|
||||
}
|
||||
const judgeColumn = {
|
||||
title: this.$i18n.t('m.Option'),
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
width: 90,
|
||||
render: (h, params) => {
|
||||
return h('Button', {
|
||||
.catch(() => {
|
||||
this.loadingTable = false;
|
||||
});
|
||||
},
|
||||
// 改变route, 通过监听route变化请求数据,这样可以产生route history, 用户返回时就会保存之前的状态
|
||||
changeRoute() {
|
||||
let query = utils.filterEmptyValue(this.buildQuery());
|
||||
query.contestID = this.contestID;
|
||||
query.problemID = this.problemID;
|
||||
let routeName = query.contestID
|
||||
? "contest-submission-list"
|
||||
: "submission-list";
|
||||
this.$router.push({
|
||||
name: routeName,
|
||||
query: utils.filterEmptyValue(query),
|
||||
});
|
||||
},
|
||||
goRoute(route) {
|
||||
this.$router.push(route);
|
||||
},
|
||||
adjustRejudgeColumn() {
|
||||
if (!this.rejudgeColumnVisible || this.rejudge_column) {
|
||||
return;
|
||||
}
|
||||
const judgeColumn = {
|
||||
title: this.$i18n.t("m.Option"),
|
||||
fixed: "right",
|
||||
align: "center",
|
||||
width: 90,
|
||||
render: (h, params) => {
|
||||
return h(
|
||||
"Button",
|
||||
{
|
||||
props: {
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
loading: params.row.loading
|
||||
type: "primary",
|
||||
size: "small",
|
||||
loading: params.row.loading,
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
this.handleRejudge(params.row.id, params.index)
|
||||
}
|
||||
}
|
||||
}, this.$i18n.t('m.Rejudge'))
|
||||
}
|
||||
}
|
||||
this.columns.push(judgeColumn)
|
||||
this.rejudge_column = true
|
||||
},
|
||||
handleStatusChange (status) {
|
||||
this.page = 1
|
||||
this.formFilter.status = status
|
||||
this.changeRoute()
|
||||
},
|
||||
handleQueryChange () {
|
||||
this.page = 1
|
||||
this.changeRoute()
|
||||
},
|
||||
handleRejudge (id, index) {
|
||||
this.submissions[index].loading = true
|
||||
api.submissionRejudge(id).then(res => {
|
||||
this.submissions[index].loading = false
|
||||
this.$success('Succeeded')
|
||||
this.getSubmissions()
|
||||
}, () => {
|
||||
this.submissions[index].loading = false
|
||||
})
|
||||
},
|
||||
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';
|
||||
this.handleRejudge(params.row.id, params.index);
|
||||
},
|
||||
},
|
||||
},
|
||||
this.$i18n.t("m.Rejudge")
|
||||
);
|
||||
},
|
||||
};
|
||||
this.columns.push(judgeColumn);
|
||||
this.rejudge_column = true;
|
||||
},
|
||||
handleStatusChange(status) {
|
||||
this.page = 1;
|
||||
this.formFilter.status = status;
|
||||
this.changeRoute();
|
||||
},
|
||||
handleQueryChange() {
|
||||
this.page = 1;
|
||||
this.changeRoute();
|
||||
},
|
||||
handleRejudge(id, index) {
|
||||
this.submissions[index].loading = true;
|
||||
api.submissionRejudge(id).then(
|
||||
(res) => {
|
||||
this.submissions[index].loading = false;
|
||||
this.$success("Succeeded");
|
||||
this.getSubmissions();
|
||||
},
|
||||
() => {
|
||||
this.submissions[index].loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
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']),
|
||||
title () {
|
||||
if (!this.contestID) {
|
||||
return 'Status'
|
||||
} else if (this.problemID) {
|
||||
return 'Problem 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
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["isAuthenticated", "userInfo"]),
|
||||
title() {
|
||||
if (!this.contestID) {
|
||||
return "Status";
|
||||
} else if (this.problemID) {
|
||||
return "Problem Submissions";
|
||||
} else {
|
||||
return "Submissions";
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route' (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
'rejudgeColumnVisible' () {
|
||||
this.adjustRejudgeColumn()
|
||||
},
|
||||
'isAuthenticated' () {
|
||||
this.init()
|
||||
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: {
|
||||
$route(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
rejudgeColumnVisible() {
|
||||
this.adjustRejudgeColumn();
|
||||
},
|
||||
isAuthenticated() {
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped >
|
||||
@media only screen and (max-width: 767px) {
|
||||
.search {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px){
|
||||
.search{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-container #main {
|
||||
flex: auto;
|
||||
margin-right: 18px;
|
||||
}
|
||||
.flex-container .filter {
|
||||
margin-right: -10px;
|
||||
}
|
||||
.flex-container #contest-menu {
|
||||
flex: none;
|
||||
width: 210px;
|
||||
}
|
||||
/deep/ .el-card__header {
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
/deep/ .el-button{
|
||||
padding: 0;
|
||||
}
|
||||
/deep/ .el-dialog {
|
||||
border-radius: 6px !important;
|
||||
text-align: center;
|
||||
}
|
||||
/deep/ .el-switch{
|
||||
padding-top: 6px;
|
||||
}
|
||||
.flex-container #main {
|
||||
flex: auto;
|
||||
}
|
||||
.flex-container .filter {
|
||||
margin-right: -10px;
|
||||
}
|
||||
.flex-container #contest-menu {
|
||||
flex: none;
|
||||
width: 210px;
|
||||
}
|
||||
/deep/ .el-card__header {
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
/deep/ .el-button {
|
||||
padding: 0;
|
||||
}
|
||||
/deep/ .el-dialog {
|
||||
border-radius: 6px !important;
|
||||
text-align: center;
|
||||
}
|
||||
/deep/ .el-switch {
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue