增加最近7天提交排行榜,首页布局修改

This commit is contained in:
Himit_ZH 2021-01-15 17:39:52 +08:00
parent 6e666b948a
commit f3941b2f0e
18 changed files with 272 additions and 108 deletions

View File

@ -58,16 +58,17 @@ public class AdminContestController {
if (currentPage == null || currentPage < 1) currentPage = 1;
if (limit == null || limit < 1) limit = 10;
IPage<Contest> iPage = new Page<>(currentPage,limit);
IPage<Contest> contestList = null;
if (!StringUtils.isEmpty(keyword)) {
QueryWrapper<Contest> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(keyword)) {
queryWrapper
.like("title", keyword).or()
.like("id", keyword).orderByDesc("gmt_create");
contestList = contestService.page(iPage, queryWrapper);
}else{
contestList = contestService.page(iPage);
.like("id", keyword);
}
queryWrapper.orderByDesc("start_time");
IPage<Contest> contestList = contestService.page(iPage, queryWrapper);
if (contestList.getTotal() == 0) { // 未查询到一条数据
return CommonResult.successResponse(contestList,"暂无数据");
} else {

View File

@ -9,10 +9,12 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.hcode.hoj.common.result.CommonResult;
import top.hcode.hoj.dao.ContestMapper;
import top.hcode.hoj.pojo.vo.ACMRankVo;
import top.hcode.hoj.pojo.vo.AnnouncementVo;
import top.hcode.hoj.pojo.vo.ConfigVo;
import top.hcode.hoj.pojo.vo.ContestVo;
import top.hcode.hoj.service.impl.AnnouncementServiceImpl;
import top.hcode.hoj.service.impl.UserRecordServiceImpl;
import java.util.List;
@ -34,6 +36,9 @@ public class HomeController {
@Autowired
private AnnouncementServiceImpl announcementDao;
@Autowired
private UserRecordServiceImpl userRecordService;
/**
* @MethodName getRecentContest
* @Params * @param null
@ -49,6 +54,21 @@ public class HomeController {
}
/**
* @MethodName getRecentSevenACRank
* @Params * @param null
* @Description 获取最近7天用户做题榜单
* @Return
* @Since 2021/1/15
*/
@GetMapping("/get-recent-seven-ac-rank")
public CommonResult getRecentSevenACRank(){
List<ACMRankVo> recent7ACRank = userRecordService.getRecent7ACRank();
return CommonResult.successResponse(recent7ACRank,"获取成功!");
}
/**
* @MethodName getCommonAnnouncement
* @Params * @param null

View File

@ -24,6 +24,8 @@ import java.util.List;
@Repository
public interface UserRecordMapper extends BaseMapper<UserRecord> {
List<ACMRankVo> getACMRankList(IPage page);
List<ACMRankVo> getRecent7ACRank();
List<OIRankVo> getOIRankList(IPage page);
UserHomeVo getUserHomeInfo(@Param("uid") String uid);
}

View File

@ -15,15 +15,15 @@
and c.type = #{type}
</if>
</where>
order by c.gmt_create DESC
order by c.start_time DESC
</select>
<select id="getContestInfoById" resultType="top.hcode.hoj.pojo.vo.ContestVo" useCache="true">
select c.id,c.author,c.title,c.type,c.status,c.description,c.seal_rank,c.seal_rank_time,c.source,c.auth,c.start_time,c.end_time,c.duration
from contest c where c.id = #{cid}
</select>
<select id="getWithinNext14DaysContests" resultType="top.hcode.hoj.pojo.vo.ContestVo">
SELECT c.id,c.author,c.title,c.type,c.source,c.auth,c.status,c.start_time,c.end_time,c.duration
FROM contest c WHERE DATE_ADD(CURDATE(), INTERVAL 14 DAY) >= DATE(start_time) AND STATUS != 1
SELECT c.id,c.author,c.title,c.type,c.source,c.auth,c.status,c.description,c.start_time,c.end_time,c.duration
FROM contest c WHERE DATE_ADD(CURDATE(), INTERVAL 14 DAY) >= DATE(start_time) AND c.status != 1
order by c.start_time ASC
</select>
</mapper>

View File

@ -8,6 +8,20 @@
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY ac DESC,solved DESC
</select>
<select id="getRecent7ACRank" resultType="top.hcode.hoj.pojo.vo.ACMRankVo" >
SELECT u.uuid as uid,u.nickname,u.username,u.signature,ur.submissions as total,ur.rating,
(SELECT COUNT( DISTINCT pid ) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY) ) AS solved,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY)) AS ac
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY ac DESC,solved DESC LIMIT 10
</select>
<select id="getOIRankList" resultType="top.hcode.hoj.pojo.vo.OIRankVo" useCache="true">
SELECT u.uuid as uid,u.nickname,u.username,u.signature,ur.submissions as total,ur.rating,
ur.total_score as score,

View File

@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
import top.hcode.hoj.pojo.vo.OIRankVo;
import top.hcode.hoj.pojo.vo.UserHomeVo;
import java.util.List;
/**
* <p>
* 服务类
@ -19,6 +21,8 @@ public interface UserRecordService extends IService<UserRecord> {
Page<ACMRankVo> getACMRankList(int limit, int currentPage);
List<ACMRankVo> getRecent7ACRank();
Page<OIRankVo> getOIRankList(int limit, int currentPage);
UserHomeVo getUserHomeInfo(String uid);

View File

@ -11,6 +11,8 @@ import top.hcode.hoj.service.UserRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 服务实现类
@ -34,6 +36,11 @@ public class UserRecordServiceImpl extends ServiceImpl<UserRecordMapper, UserRec
return page.setRecords(userRecordMapper.getACMRankList(page));
}
@Override
public List<ACMRankVo> getRecent7ACRank() {
return userRecordMapper.getRecent7ACRank();
}
@Override
public Page<OIRankVo> getOIRankList(int limit, int currentPage) {
//新建分页

View File

@ -15,15 +15,15 @@
and c.type = #{type}
</if>
</where>
order by c.gmt_create DESC
order by c.start_time DESC
</select>
<select id="getContestInfoById" resultType="top.hcode.hoj.pojo.vo.ContestVo" useCache="true">
select c.id,c.author,c.title,c.type,c.status,c.description,c.seal_rank,c.seal_rank_time,c.source,c.auth,c.start_time,c.end_time,c.duration
from contest c where c.id = #{cid}
</select>
<select id="getWithinNext14DaysContests" resultType="top.hcode.hoj.pojo.vo.ContestVo">
SELECT c.id,c.author,c.title,c.type,c.source,c.auth,c.status,c.start_time,c.end_time,c.duration
FROM contest c WHERE DATE_ADD(CURDATE(), INTERVAL 14 DAY) >= DATE(start_time) AND STATUS != 1
SELECT c.id,c.author,c.title,c.type,c.source,c.auth,c.status,c.description,c.start_time,c.end_time,c.duration
FROM contest c WHERE DATE_ADD(CURDATE(), INTERVAL 14 DAY) >= DATE(start_time) AND c.status != 1
order by c.start_time ASC
</select>
</mapper>

View File

@ -8,6 +8,20 @@
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY ac DESC,solved DESC
</select>
<select id="getRecent7ACRank" resultType="top.hcode.hoj.pojo.vo.ACMRankVo" >
SELECT u.uuid as uid,u.nickname,u.username,u.signature,ur.submissions as total,ur.rating,
(SELECT COUNT( DISTINCT pid ) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY) ) AS solved,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY)) AS ac
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY ac DESC,solved DESC LIMIT 10
</select>
<select id="getOIRankList" resultType="top.hcode.hoj.pojo.vo.OIRankVo" useCache="true">
SELECT u.uuid as uid,u.nickname,u.username,u.signature,ur.submissions as total,ur.rating,
ur.total_score as score,

View File

@ -126,6 +126,10 @@ const ojApi = {
params
})
},
getRecent7ACRank(){
return ajax('/api/get-recent-seven-ac-rank', 'get', {
})
},
getRecentOtherContests(){
},

View File

@ -132,9 +132,9 @@ export const PROBLEM_LEVEL_RESERVE={
export const CONTEST_STATUS = {
'SCHEDULED': '-1',
'RUNNING': '0',
'ENDED': '1'
'SCHEDULED': -1,
'RUNNING': 0,
'ENDED': 1
}
export const CONTEST_STATUS_REVERSE = {

View File

@ -1,7 +1,7 @@
import moment from 'moment'
import api from '@/common/api'
import { CONTEST_STATUS, USER_TYPE, CONTEST_TYPE } from '@/common/constants'
import time from '@/common/time'
const state = {
now: moment(),
intoAccess: false, // 私有比赛进入权限
@ -86,19 +86,21 @@ const getters = {
countdown: (state, getters) => {
// 还未开始的显示
if (getters.contestStatus === CONTEST_STATUS.SCHEDULED) {
let duration = moment.duration(getters.contestStartTime.diff(state.now, 'seconds'), 'seconds')
let durationMs = getters.contestStartTime.diff(state.now, 'seconds')
let duration = moment.duration(durationMs, 'seconds')
// time is too long
if (duration.weeks() > 0) {
return 'Start At ' + duration.humanize()
}
let texts = [Math.floor(duration.asHours()), duration.minutes(), duration.seconds()]
return '-' + texts.join(':')
let texts = time.secondFormat(durationMs)
return '-' + texts
// 比赛进行中的显示
} else if (getters.contestStatus === CONTEST_STATUS.RUNNING) {
let duration = moment.duration(getters.contestEndTime.diff(state.now, 'seconds'), 'seconds')
// 倒计时文本显示
let texts = [Math.floor(duration.asHours()), duration.minutes(), duration.seconds()]
return '-' + texts.join(':')
let texts = time.secondFormat(getters.contestEndTime.diff(state.now, 'seconds'))
return '-' + texts
} else {
return 'Ended'
}

View File

@ -1,27 +1,53 @@
<template>
<div>
<el-card class="contest" v-if="contests.length">
<div slot="header" class="clearfix title">
<el-link @click="goContest" :underline="false">{{contests[index].title}}</el-link>
<el-row :gutter="20">
<el-col :md="14" :sm="24">
<el-card v-if="contests.length">
<div slot="header" class="clearfix title content-center">
<el-link @click="goContest" :underline="false">{{
contests[index].title
}}</el-link>
<div class="contest-status">
<el-tag
effect="dark"
size="medium"
:color="CONTEST_STATUS_REVERSE[contests[index].status]['color']"
>
<i class="fa fa-circle" aria-hidden="true"></i>
{{ CONTEST_STATUS_REVERSE[contests[index].status]['name'] }}
</el-tag>
</div>
</div>
<el-carousel
indicator-position="outside"
height="350px"
height="430px"
:interval="interval"
v-model="index"
>
<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" style="margin-top: 4px;"
<div class="contest-tags content-center">
<el-button
type="primary"
round
size="mini"
style="margin-top: 4px;"
><i class="fa fa-calendar"></i>
{{ contest.startTime | localtime }}
</el-button>
<el-button type="success" round size="mini" style="margin-top: 4px;"
<el-button
type="success"
round
size="mini"
style="margin-top: 4px;"
><i class="fa fa-clock-o"></i>
{{ getDuration(contest.startTime,contest.endTime) }}
{{ getDuration(contest.startTime, contest.endTime) }}
</el-button>
<el-button type="warning" round size="mini" style="margin-top: 4px;"
<el-button
type="warning"
round
size="mini"
style="margin-top: 4px;"
><i class="fa fa-trophy"></i>
{{ contest.type | parseContestType }}
</el-button>
@ -33,47 +59,104 @@
</el-carousel-item>
</el-carousel>
</el-card>
<el-row :gutter="20">
<el-col :md="12" :sm="24">
<Announcements></Announcements>
<el-card v-else>
<div slot="header" class="content-center">
<span class="panel-title home-title welcome-title"
>Welcome to HOJ</span
>
</div>
<div class="content-center">
<h2>
欢迎大家光临和使用本OJ
</h2>
<h3>
这是一个的前后端分离的分布式在线评测系统
</h3>
<h3>基于Vue.js,Springboot和SpringCloud.</h3>
<h1>待续....</h1>
</div>
</el-card>
</el-col>
<el-col :md="12" :sm="24">
<el-card class="box-card">
<el-col :md="10" :sm="24">
<el-card>
<div slot="header" class="clearfix">
<span class="panel-title home-title"
>Other OnlineJudge Contest</span
>Recent 7 Days AC Top 10 Rank</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>
align="center"
:data="recentUserACRecord"
max-height="460px"
>
<vxe-table-column type="seq" min-width="30"></vxe-table-column>
<vxe-table-column
field="username"
title="Username"
min-width="130"
></vxe-table-column>
<vxe-table-column field="ac" title="AC" min-width="30">
</vxe-table-column>
<vxe-table-column field="solved" title="Solved" min-width="30">
</vxe-table-column>
</vxe-table>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 25px;">
<el-col :md="14" :sm="24">
<el-card>
<div slot="header" class="clearfix">
<span class="panel-title home-title"
>Other Online Judge 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 v-slot="{ row }">
<span>{{ row.endTime | localtime }}</span>
</template>
</vxe-table-column>
</vxe-table>
</el-card>
</el-col>
<el-col :md="10" :sm="24">
<Announcements></Announcements>
</el-col>
</el-row>
</div>
</template>
<script>
import time from "@/common/time";
import api from '@/common/api'
import Announcements from "@/components/oj/common/Announcements.vue";
import time from '@/common/time';
import api from '@/common/api';
import Announcements from '@/components/oj/common/Announcements.vue';
import { CONTEST_STATUS_REVERSE } from '@/common/constants';
import { mapState } from 'vuex';
export default {
name: "home",
name: 'home',
components: {
Announcements,
},
@ -82,48 +165,53 @@ export default {
interval: 6000,
otherContests: [
{
oj: "Codeforces",
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",
'Codeforces Round #680 (Div. 1, based on VK Cup 2020-2021 - Final)',
beginTime: '2020-11-08T05:00:00Z',
endTime: '2020-11-08T08:00:00Z',
},
],
recentUserACRecord: [],
CONTEST_STATUS_REVERSE: {},
contests: [],
index:0,
index: 0,
};
},
mounted(){
this.getRecentContests()
mounted() {
this.CONTEST_STATUS_REVERSE = Object.assign({}, CONTEST_STATUS_REVERSE);
this.getRecentContests();
this.getRecent7ACRank();
// this.getRecentOtherContests()
},
methods: {
getRecentContests(){
api.getRecentContests().then(res=>{
this.contests = res.data.data
})
getRecentContests() {
api.getRecentContests().then((res) => {
this.contests = res.data.data;
});
},
getRecentOtherContests(){
api.getRecentOtherContests().then(res=>{
this.otherContests = res.data.data
})
getRecentOtherContests() {
api.getRecentOtherContests().then((res) => {
this.otherContests = res.data.data;
});
},
getRecent7ACRank() {
api.getRecent7ACRank().then((res) => {
this.recentUserACRecord = res.data.data;
});
},
goContest() {
this.$router.push({
name: 'contest-details',
params: {contestID: this.contests[this.index].id}
})
name: 'ContestDetails',
params: { contestID: this.contests[this.index].id },
});
},
getDuration (startTime, endTime) {
return time.duration(startTime, endTime)
getDuration(startTime, endTime) {
return time.duration(startTime, endTime);
},
},
filters: {
localtime(value) {
return time.utcToLocal(value);
},
computed: {
...mapState(['websiteConfig']),
},
};
</script>
@ -139,25 +227,35 @@ export default {
background-color: #fff;
}
.el-carousel__item {
overflow-y: auto!important;
overflow-y: auto !important;
}
.el-col {
margin-top: 25px;
}
.contest {
.content-center {
text-align: center;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
content: '';
}
.clearfix:after {
clear: both;
}
.contest .title .el-link {
.welcome-title {
font-weight: 600;
font-size: 25px;
}
.contest-status {
float: right;
}
@media screen and (max-width: 1080px) {
.contest-status {
text-align: center;
float: none;
margin-top: 5px;
}
}
.title .el-link {
font-size: 21px;
font-weight: 500;
color: #444;
}
@ -173,7 +271,5 @@ export default {
}
.contest .contest-description {
margin-top: 25px;
/* text-align: left; */
}
</style>