修正自动删除测试数据的bug 优化排行榜显示
This commit is contained in:
parent
dcf7d38c7a
commit
760aa4f61b
|
@ -1,112 +0,0 @@
|
|||
package top.hcode.hoj.judge.self;
|
||||
|
||||
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
|
||||
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
|
||||
import com.alibaba.cloud.nacos.ribbon.NacosServer;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.naming.NamingService;
|
||||
import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
|
||||
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import top.hcode.hoj.pojo.entity.JudgeServer;
|
||||
import top.hcode.hoj.service.impl.JudgeServerServiceImpl;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/2/4 16:44
|
||||
* @Description: 任务调度的自定义负载均衡策略
|
||||
*/
|
||||
@Slf4j
|
||||
|
||||
public class JudgeChooseRule extends AbstractLoadBalancerRule {
|
||||
|
||||
@Autowired
|
||||
private NacosDiscoveryProperties discoveryProperties;
|
||||
|
||||
|
||||
private JudgeServerServiceImpl judgeServerService ;
|
||||
|
||||
|
||||
@Autowired
|
||||
public void setJudgeServerService (JudgeServerServiceImpl judgeServerService){
|
||||
this.judgeServerService = judgeServerService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initWithNiwsConfig(IClientConfig iClientConfig) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server choose(Object key) {
|
||||
// // 获取配置文件中所配置的集群名称
|
||||
// String clusterName = discoveryProperties.getClusterName();
|
||||
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
|
||||
// 需要请求的微服务名称
|
||||
String serviceId = loadBalancer.getName();
|
||||
// 获取该微服务的所有健康实例
|
||||
List<Instance> instances = getInstances(serviceId);
|
||||
if (instances.size() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> keyList = new ArrayList<>();
|
||||
// 获取当前健康实例取出ip和port拼接
|
||||
for (Instance instance : instances) {
|
||||
keyList.add(instance.getIp() + ":" + instance.getPort());
|
||||
}
|
||||
|
||||
// 过滤出小于或等于规定最大并发判题任务数的服务实例且健康的判题机
|
||||
QueryWrapper<JudgeServer> judgeServerQueryWrapper = new QueryWrapper<>();
|
||||
judgeServerQueryWrapper
|
||||
.in("url", keyList)
|
||||
.orderByAsc("task_num");
|
||||
List<JudgeServer> judgeServerList = judgeServerService.list(judgeServerQueryWrapper);
|
||||
System.out.println(judgeServerList);
|
||||
// 使用乐观锁获取可用判题机
|
||||
for (JudgeServer judgeServer : judgeServerList) {
|
||||
if (judgeServer.getTaskNumber() <= judgeServer.getMaxTaskNumber()) {
|
||||
judgeServer.setTaskNumber(judgeServer.getTaskNumber() + 1);
|
||||
boolean isOk = judgeServerService.updateById(judgeServer);
|
||||
if (isOk) {
|
||||
int instanceIndex = keyList.indexOf(judgeServer.getIp() + ":" + judgeServer.getPort());
|
||||
if (instanceIndex != -1) {
|
||||
return new NacosServer(instances.get(instanceIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private List<Instance> getInstances(String serviceId) {
|
||||
// 获取服务发现的相关API
|
||||
NamingService namingService = discoveryProperties.namingServiceInstance();
|
||||
try {
|
||||
// 获取该微服务的所有健康实例
|
||||
return namingService.selectInstances(serviceId, true);
|
||||
} catch (NacosException e) {
|
||||
log.error("获取微服务健康实例发生异常--------->{}", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -109,7 +109,7 @@ public class ScheduleServiceImpl implements ScheduleService {
|
|||
// @Scheduled(cron = "0/5 * * * * *")
|
||||
@Override
|
||||
public void deleteTestCase() {
|
||||
boolean result = FileUtil.del(Constants.File.TESTCASE_BASE_FOLDER.getPath());
|
||||
boolean result = FileUtil.del(Constants.File.TESTCASE_TMP_FOLDER.getPath());
|
||||
if (!result){
|
||||
log.error("每日定时任务异常------------------------>{}","清除本地的题目测试数据失败!");
|
||||
}
|
||||
|
|
|
@ -90,15 +90,6 @@ logging:
|
|||
naming: error
|
||||
root: error
|
||||
|
||||
# 开启Hystrix断路器
|
||||
feign:
|
||||
hystrix:
|
||||
enabled: true
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
connectTimeout: 15000
|
||||
readTimeout: 60000
|
||||
hystrix:
|
||||
command:
|
||||
default:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
hoj-judge-server:
|
||||
max-task-num: -1 # -1表示最大并行任务数为cpu核心数*2
|
||||
ip: 39.108.148.172 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
ip: 127.0.0.1 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
port: 8088 # 端口号
|
||||
name: hoj-judger-1 # 判题机名字 唯一不可重复!!!
|
||||
nacos-url: 129.204.177.72:8848 # nacos地址
|
||||
nacos-url: 127.0.0.1:8848 # nacos地址
|
||||
remote-judge:
|
||||
open: true # 当前判题服务器是否开启远程虚拟判题功能
|
||||
max-task-num: -1 # -1表示最大并行任务数为(cpu核心数*2)*2
|
||||
|
|
|
@ -195,9 +195,7 @@
|
|||
<el-tag
|
||||
effect="dark"
|
||||
:color="submissionStatus.color"
|
||||
@click.native="
|
||||
handleRoute('/submission-detail/' + submissionId)
|
||||
"
|
||||
@click.native="submissionRoute"
|
||||
>
|
||||
<i class="fa fa-circle" aria-hidden="true"></i>
|
||||
{{ submissionStatus.text }}
|
||||
|
@ -739,15 +737,20 @@ export default {
|
|||
},
|
||||
submissionRoute() {
|
||||
if (this.contestID) {
|
||||
return {
|
||||
name: 'contest-submission-list',
|
||||
query: { problemID: this.problemID },
|
||||
};
|
||||
// 比赛提交详情
|
||||
this.$router.push({
|
||||
name: 'ContestSubmissionDeatil',
|
||||
params: {
|
||||
contestID: this.contestID,
|
||||
problemID: this.problemID,
|
||||
submitID: this.submissionId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return {
|
||||
name: 'submission-list',
|
||||
query: { problemID: this.problemID },
|
||||
};
|
||||
this.$router.push({
|
||||
name: 'SubmissionDeatil',
|
||||
params: { submitID: this.submissionId },
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,204 +1,244 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="space-around">
|
||||
<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" :autoresize="true"></ECharts>
|
||||
</div>
|
||||
</el-card>
|
||||
<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 @click="getInfoByUsername(row.uid,row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||
</template>
|
||||
</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="solved" title="Solved" min-width="80"></vxe-table-column>
|
||||
<vxe-table-column title="AC" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<a @click="goUserACStatus(row.username)" style="color:rgb(87, 163, 243);">{{row.ac}}</a>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||
<vxe-table-column title="Rating" min-width="80">
|
||||
<template v-slot="{row}">
|
||||
<span>{{getACRate(row.ac,row.total)}}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
|
||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||
@on-change="getRankData" show-sizer
|
||||
@on-page-size-change="getRankData(1)"></Pagination>
|
||||
<el-card :padding="10">
|
||||
<div slot="header"><span class="panel-title">ACM Ranklist</span></div>
|
||||
<div class="echarts">
|
||||
<ECharts :options="options" ref="chart" :autoresize="true"></ECharts>
|
||||
</div>
|
||||
</el-card>
|
||||
<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
|
||||
@click="getInfoByUsername(row.uid, row.username)"
|
||||
style="color:rgb(87, 163, 243);"
|
||||
>{{ row.username }}</a
|
||||
>
|
||||
</template>
|
||||
</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="solved"
|
||||
title="Solved"
|
||||
min-width="80"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column title="AC" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<a
|
||||
@click="goUserACStatus(row.username)"
|
||||
style="color:rgb(87, 163, 243);"
|
||||
>{{ row.ac }}</a
|
||||
>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="total"
|
||||
title="Total"
|
||||
min-width="80"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column title="Rating" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ getACRate(row.ac, row.total) }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
|
||||
<Pagination
|
||||
:total="total"
|
||||
:page-size.sync="limit"
|
||||
:current.sync="page"
|
||||
@on-change="getRankData"
|
||||
show-sizer
|
||||
@on-page-size-change="getRankData(1)"
|
||||
></Pagination>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/common/api'
|
||||
import Pagination from '@/components/oj/common/Pagination'
|
||||
import utils from '@/common/utils'
|
||||
import { RULE_TYPE } from '@/common/constants'
|
||||
import api from '@/common/api';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import utils from '@/common/utils';
|
||||
import { RULE_TYPE } from '@/common/constants';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
Pagination
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
page: 1,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
loadingTable: false,
|
||||
dataRank: [],
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
Pagination,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: 1,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
loadingTable: false,
|
||||
dataRank: [],
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
data: ['AC', 'Total'],
|
||||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%',
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: true },
|
||||
magicType: { show: true, type: ['line', 'bar', 'stack'] },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
legend: {
|
||||
data: ['AC', 'Total']
|
||||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%',
|
||||
left:'8%',
|
||||
right:'8%'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {show: true, readOnly: true},
|
||||
magicType: {show: true, type: ['line', 'bar', 'stack']},
|
||||
saveAsImage: {show: true}
|
||||
},
|
||||
right: '8%',
|
||||
top:'5%'
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['root'],
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
showMinLabel: true,
|
||||
showMaxLabel: true,
|
||||
align: 'center',
|
||||
formatter: (value, index) => {
|
||||
return utils.breakLongWords(value, 13)
|
||||
right: '8%',
|
||||
top: '5%',
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['root'],
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
showMinLabel: true,
|
||||
showMaxLabel: true,
|
||||
align: 'center',
|
||||
formatter: (value, index) => {
|
||||
if (this.isAuthenticated && this.userInfo.username == value) {
|
||||
return utils.breakLongWords(value, 14);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate:50,
|
||||
textStyle:{
|
||||
fontSize:'12em',
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'AC',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
itemStyle: {
|
||||
color:'#91c7ae'
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{type: 'max', name: 'max'}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Total',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
itemStyle:{
|
||||
color:'#6ab0b8'
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate: 50,
|
||||
textStyle: {
|
||||
fontSize: '12em',
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{type: 'max', name: 'max'}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getRankData(1)
|
||||
},
|
||||
methods: {
|
||||
getRankData (page) {
|
||||
let bar = this.$refs.chart
|
||||
bar.showLoading({maskColor: 'rgba(250, 250, 250, 0.8)'})
|
||||
this.loadingTable = true
|
||||
api.getUserRank(page, this.limit, RULE_TYPE.ACM).then(res => {
|
||||
this.loadingTable = false
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'AC',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
itemStyle: {
|
||||
color: '#91c7ae',
|
||||
},
|
||||
markPoint: {
|
||||
data: [{ type: 'max', name: 'max' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Total',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
itemStyle: {
|
||||
color: '#6ab0b8',
|
||||
},
|
||||
markPoint: {
|
||||
data: [{ type: 'max', name: 'max' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getRankData(1);
|
||||
},
|
||||
methods: {
|
||||
getRankData(page) {
|
||||
let bar = this.$refs.chart;
|
||||
bar.showLoading({ maskColor: 'rgba(250, 250, 250, 0.8)' });
|
||||
this.loadingTable = true;
|
||||
api
|
||||
.getUserRank(page, this.limit, RULE_TYPE.ACM)
|
||||
.then((res) => {
|
||||
this.loadingTable = false;
|
||||
if (page === 1) {
|
||||
this.changeCharts(res.data.data.records.slice(0, 10))
|
||||
this.changeCharts(res.data.data.records.slice(0, 10));
|
||||
}
|
||||
this.total = res.data.data.total
|
||||
this.dataRank = res.data.data.records
|
||||
bar.hideLoading()
|
||||
}).catch(() => {
|
||||
this.loadingTable = false
|
||||
bar.hideLoading()
|
||||
this.total = res.data.data.total;
|
||||
this.dataRank = res.data.data.records;
|
||||
bar.hideLoading();
|
||||
})
|
||||
},
|
||||
changeCharts (rankData) {
|
||||
let [usernames, acData, totalData] = [[], [], []]
|
||||
rankData.forEach(ele => {
|
||||
usernames.push(ele.username)
|
||||
acData.push(ele.ac)
|
||||
totalData.push(ele.total)
|
||||
})
|
||||
this.options.xAxis[0].data = usernames
|
||||
this.options.series[0].data = acData
|
||||
this.options.series[1].data = totalData
|
||||
},
|
||||
getInfoByUsername(uid,username){
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: {uid,username},
|
||||
.catch(() => {
|
||||
this.loadingTable = false;
|
||||
bar.hideLoading();
|
||||
});
|
||||
},
|
||||
goUserACStatus(username){
|
||||
this.$router.push({
|
||||
path: '/status',
|
||||
query: {username,status:0},
|
||||
});
|
||||
},
|
||||
getACRate(ac,total){
|
||||
return utils.getACRate(ac,total)
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
|
||||
}
|
||||
}
|
||||
changeCharts(rankData) {
|
||||
let [usernames, acData, totalData] = [[], [], []];
|
||||
rankData.forEach((ele) => {
|
||||
usernames.push(ele.username);
|
||||
acData.push(ele.ac);
|
||||
totalData.push(ele.total);
|
||||
});
|
||||
this.options.xAxis[0].data = usernames;
|
||||
this.options.series[0].data = acData;
|
||||
this.options.series[1].data = totalData;
|
||||
},
|
||||
getInfoByUsername(uid, username) {
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: { uid, username },
|
||||
});
|
||||
},
|
||||
goUserACStatus(username) {
|
||||
this.$router.push({
|
||||
path: '/status',
|
||||
query: { username, status: 0 },
|
||||
});
|
||||
},
|
||||
getACRate(ac, total) {
|
||||
return utils.getACRate(ac, total);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'userInfo']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0!important;
|
||||
}
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,187 +1,232 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="space-around">
|
||||
<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 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 @click="getInfoByUsername(row.uid,row.username)" style="color:rgb(87, 163, 243);">{{row.username}}</a>
|
||||
</template>
|
||||
</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 title="AC" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<a @click="goUserACStatus(row.username)" style="color:rgb(87, 163, 243);">{{row.ac}}</a>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="total" title="Total" min-width="80"></vxe-table-column>
|
||||
<vxe-table-column title="Rating" min-width="80">
|
||||
<template v-slot="{row}">
|
||||
<span>{{getACRate(row.ac,row.total)}}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
<Pagination :total="total" :page-size.sync="limit" :current.sync="page"
|
||||
@on-change="getRankData"
|
||||
show-sizer @on-page-size-change="getRankData(1)"></Pagination>
|
||||
<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
|
||||
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
|
||||
@click="getInfoByUsername(row.uid, row.username)"
|
||||
style="color:rgb(87, 163, 243);"
|
||||
>{{ row.username }}</a
|
||||
>
|
||||
</template>
|
||||
</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 title="AC" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<a
|
||||
@click="goUserACStatus(row.username)"
|
||||
style="color:rgb(87, 163, 243);"
|
||||
>{{ row.ac }}</a
|
||||
>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="total"
|
||||
title="Total"
|
||||
min-width="80"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column title="Rating" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ getACRate(row.ac, row.total) }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
:page-size.sync="limit"
|
||||
:current.sync="page"
|
||||
@on-change="getRankData"
|
||||
show-sizer
|
||||
@on-page-size-change="getRankData(1)"
|
||||
></Pagination>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/common/api'
|
||||
import Pagination from '@/components/oj/common/Pagination'
|
||||
import utils from '@/common/utils'
|
||||
import { RULE_TYPE } from '@/common/constants'
|
||||
import api from '@/common/api';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import utils from '@/common/utils';
|
||||
import { RULE_TYPE } from '@/common/constants';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
Pagination
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
page: 1,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
dataRank:[],
|
||||
loadingTable:false,
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
Pagination,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: 1,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
dataRank: [],
|
||||
loadingTable: false,
|
||||
options: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
data: ['Score'],
|
||||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%',
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: true },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
legend: {
|
||||
data: ['Score']
|
||||
},
|
||||
grid: {
|
||||
x: '3%',
|
||||
x2: '3%',
|
||||
left:'8%',
|
||||
right:'8%'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {show: true, readOnly: true},
|
||||
magicType: {show: true, type: ['line', 'bar']},
|
||||
saveAsImage: {show: true}
|
||||
},
|
||||
right: '8%',
|
||||
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)
|
||||
right: '8%',
|
||||
top: '5%',
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['root'],
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
showMinLabel: true,
|
||||
showMaxLabel: true,
|
||||
align: 'center',
|
||||
formatter: (value, index) => {
|
||||
if (this.isAuthenticated && this.userInfo.username == value) {
|
||||
return utils.breakLongWords(value, 14);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate:50,
|
||||
textStyle:{
|
||||
fontSize:'12em',
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Score',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
barMaxWidth: '80',
|
||||
markPoint: {
|
||||
data: [
|
||||
{type: 'max', name: 'max'}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getRankData(1)
|
||||
},
|
||||
methods: {
|
||||
getRankData (page) {
|
||||
let bar = this.$refs.chart
|
||||
bar.showLoading({maskColor: 'rgba(250, 250, 250, 0.8)'})
|
||||
this.loadingTable = true
|
||||
api.getUserRank(page, this.limit, RULE_TYPE.OI).then(res => {
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate: 50,
|
||||
textStyle: {
|
||||
fontSize: '12em',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Score',
|
||||
type: 'bar',
|
||||
data: [0],
|
||||
barMaxWidth: '80',
|
||||
markPoint: {
|
||||
data: [{ type: 'max', name: 'max' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getRankData(1);
|
||||
},
|
||||
methods: {
|
||||
getRankData(page) {
|
||||
let bar = this.$refs.chart;
|
||||
bar.showLoading({ maskColor: 'rgba(250, 250, 250, 0.8)' });
|
||||
this.loadingTable = true;
|
||||
api.getUserRank(page, this.limit, RULE_TYPE.OI).then(
|
||||
(res) => {
|
||||
if (page === 1) {
|
||||
this.changeCharts(res.data.data.records.slice(0, 10))
|
||||
this.changeCharts(res.data.data.records.slice(0, 10));
|
||||
}
|
||||
this.total = res.data.data.total
|
||||
this.dataRank = res.data.data.records
|
||||
this.loadingTable = false
|
||||
bar.hideLoading()
|
||||
},err=>{
|
||||
this.loadingTable = false
|
||||
bar.hideLoading()
|
||||
})
|
||||
},
|
||||
changeCharts (rankData) {
|
||||
let [usernames, scores] = [[], []]
|
||||
rankData.forEach(ele => {
|
||||
usernames.push(ele.username)
|
||||
scores.push(ele.score)
|
||||
})
|
||||
this.options.xAxis[0].data = usernames
|
||||
this.options.series[0].data = scores
|
||||
},
|
||||
getInfoByUsername(uid,username){
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: {uid,username},
|
||||
});
|
||||
},
|
||||
goUserACStatus(username){
|
||||
this.$router.push({
|
||||
path: '/status',
|
||||
query: {username,status:0},
|
||||
});
|
||||
},
|
||||
getACRate(ac,total){
|
||||
return utils.getACRate(ac,total)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.total = res.data.data.total;
|
||||
this.dataRank = res.data.data.records;
|
||||
this.loadingTable = false;
|
||||
bar.hideLoading();
|
||||
},
|
||||
(err) => {
|
||||
this.loadingTable = false;
|
||||
bar.hideLoading();
|
||||
}
|
||||
);
|
||||
},
|
||||
changeCharts(rankData) {
|
||||
let [usernames, scores] = [[], []];
|
||||
rankData.forEach((ele) => {
|
||||
usernames.push(ele.username);
|
||||
scores.push(ele.score);
|
||||
});
|
||||
this.options.xAxis[0].data = usernames;
|
||||
this.options.series[0].data = scores;
|
||||
},
|
||||
getInfoByUsername(uid, username) {
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: { uid, username },
|
||||
});
|
||||
},
|
||||
goUserACStatus(username) {
|
||||
this.$router.push({
|
||||
path: '/status',
|
||||
query: { username, status: 0 },
|
||||
});
|
||||
},
|
||||
getACRate(ac, total) {
|
||||
return utils.getACRate(ac, total);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'userInfo']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0!important;
|
||||
}
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
/deep/.el-card__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
}}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Time" min-width="64">
|
||||
<vxe-table-column title="Time" min-width="96">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ submissionTimeFormat(row.time) }}</span>
|
||||
</template>
|
||||
|
@ -81,7 +81,7 @@
|
|||
<span>{{ row.score }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Length" min-width="60">
|
||||
<vxe-table-column title="Length" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ submissionLengthFormat(row.length) }}</span>
|
||||
</template>
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="time" title="Time" min-width="64">
|
||||
<vxe-table-column field="time" title="Time" min-width="96">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ submissionTimeFormat(row.time) }}</span>
|
||||
</template>
|
||||
|
@ -163,7 +163,7 @@
|
|||
</template>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column field="length" title="Length" min-width="60">
|
||||
<vxe-table-column field="length" title="Length" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ submissionLengthFormat(row.length) }}</span>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue