|
@@ -1,5 +1,22 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="ranking-board-page">
|
|
<div class="ranking-board-page">
|
|
|
|
|
+ <!-- 右上角倒计时显示 -->
|
|
|
|
|
+ <div class="countdown-display">
|
|
|
|
|
+ <el-card shadow="never" class="countdown-card">
|
|
|
|
|
+ <div class="countdown-content">
|
|
|
|
|
+ <el-icon class="countdown-icon"><Timer /></el-icon>
|
|
|
|
|
+ <span class="countdown-text">{{ countdownSeconds }}s</span>
|
|
|
|
|
+ <!-- <el-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="stopAutoRefresh"
|
|
|
|
|
+ class="stop-btn"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon><Close /></el-icon>
|
|
|
|
|
+ </el-button> -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </div>
|
|
|
<!-- 赛事名称标题 -->
|
|
<!-- 赛事名称标题 -->
|
|
|
<div class="event-title">
|
|
<div class="event-title">
|
|
|
<h2>{{defaultEventInfo ? defaultEventInfo.eventName : '赛事排行榜'}}</h2>
|
|
<h2>{{defaultEventInfo ? defaultEventInfo.eventName : '赛事排行榜'}}</h2>
|
|
@@ -173,9 +190,9 @@
|
|
|
</el-row>
|
|
</el-row>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
-
|
|
|
|
|
|
|
+8
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed, onMounted } from 'vue';
|
|
|
|
|
|
|
+import { ref, computed, onMounted, onUnmounted, getCurrentInstance, toRefs } from 'vue';
|
|
|
import { listScoreRanking, listPersonalRanking, listTeamRanking } from '@/api/system/gameEvent/eventRank';
|
|
import { listScoreRanking, listPersonalRanking, listTeamRanking } from '@/api/system/gameEvent/eventRank';
|
|
|
import { listGameScore } from '@/api/system/gameScore';
|
|
import { listGameScore } from '@/api/system/gameScore';
|
|
|
import { listGameTeam } from '@/api/system/gameTeam';
|
|
import { listGameTeam } from '@/api/system/gameTeam';
|
|
@@ -188,6 +205,7 @@ import { storeToRefs } from 'pinia';
|
|
|
import { listRankGroup } from '@/api/system/rankGroup';
|
|
import { listRankGroup } from '@/api/system/rankGroup';
|
|
|
import { RankGroupVO } from '@/api/system/rankGroup/types';
|
|
import { RankGroupVO } from '@/api/system/rankGroup/types';
|
|
|
import { Refresh } from '@element-plus/icons-vue';
|
|
import { Refresh } from '@element-plus/icons-vue';
|
|
|
|
|
+import { Timer, Close } from '@element-plus/icons-vue';
|
|
|
|
|
|
|
|
// 定义队伍积分排行榜的数据结构
|
|
// 定义队伍积分排行榜的数据结构
|
|
|
interface TeamScore {
|
|
interface TeamScore {
|
|
@@ -233,6 +251,35 @@ const teamDisplayCount = ref(10); // 团队排行榜显示数量,默认10
|
|
|
const personalRefreshing = ref(false); // 个人排行榜刷新状态
|
|
const personalRefreshing = ref(false); // 个人排行榜刷新状态
|
|
|
const teamRefreshing = ref(false); // 团队排行榜刷新状态
|
|
const teamRefreshing = ref(false); // 团队排行榜刷新状态
|
|
|
|
|
|
|
|
|
|
+// 自动刷新相关
|
|
|
|
|
+const autoRefreshInterval = ref<NodeJS.Timeout | null>(null);
|
|
|
|
|
+const autoRefreshSeconds = 300; // 默认300秒自动刷新
|
|
|
|
|
+// 添加倒计时相关状态
|
|
|
|
|
+const countdownSeconds = ref(300); // 倒计时秒数
|
|
|
|
|
+const countdownInterval = ref<NodeJS.Timeout | null>(null); // 倒计时定时器
|
|
|
|
|
+
|
|
|
|
|
+// 启动倒计时
|
|
|
|
|
+const startCountdown = () => {
|
|
|
|
|
+ countdownSeconds.value = autoRefreshSeconds;
|
|
|
|
|
+
|
|
|
|
|
+ countdownInterval.value = setInterval(() => {
|
|
|
|
|
+ countdownSeconds.value--;
|
|
|
|
|
+
|
|
|
|
|
+ if (countdownSeconds.value <= 0) {
|
|
|
|
|
+ countdownSeconds.value = autoRefreshSeconds;
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 1000);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 停止倒计时
|
|
|
|
|
+const stopCountdown = () => {
|
|
|
|
|
+ if (countdownInterval.value) {
|
|
|
|
|
+ clearInterval(countdownInterval.value);
|
|
|
|
|
+ countdownInterval.value = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ countdownSeconds.value = 0;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 根据显示数量过滤数据
|
|
// 根据显示数量过滤数据
|
|
|
const displayedAthleteList = computed(() => {
|
|
const displayedAthleteList = computed(() => {
|
|
|
return athleteScoreList.value.slice(0, personalDisplayCount.value);
|
|
return athleteScoreList.value.slice(0, personalDisplayCount.value);
|
|
@@ -515,7 +562,8 @@ const refreshPersonalRanking = async () => {
|
|
|
const res = await listScoreRanking(defaultEventInfo.value.eventId.toString());
|
|
const res = await listScoreRanking(defaultEventInfo.value.eventId.toString());
|
|
|
// 按照totalScore字段降序排序(分数高的在前面)
|
|
// 按照totalScore字段降序排序(分数高的在前面)
|
|
|
athleteScoreList.value = res.data.sort((a, b) => b.totalScore - a.totalScore);
|
|
athleteScoreList.value = res.data.sort((a, b) => b.totalScore - a.totalScore);
|
|
|
-
|
|
|
|
|
|
|
+ // 手动刷新时重置倒计时
|
|
|
|
|
+ startCountdown();
|
|
|
// 显示成功消息
|
|
// 显示成功消息
|
|
|
ElMessage.success('个人排行榜数据已刷新');
|
|
ElMessage.success('个人排行榜数据已刷新');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
@@ -532,6 +580,8 @@ const refreshTeamRanking = async () => {
|
|
|
try {
|
|
try {
|
|
|
// 重新加载队伍积分排行榜
|
|
// 重新加载队伍积分排行榜
|
|
|
await loadTeamScores();
|
|
await loadTeamScores();
|
|
|
|
|
+ // 手动刷新时重置倒计时
|
|
|
|
|
+ startCountdown();
|
|
|
|
|
|
|
|
// 显示成功消息
|
|
// 显示成功消息
|
|
|
ElMessage.success('团队排行榜数据已刷新');
|
|
ElMessage.success('团队排行榜数据已刷新');
|
|
@@ -566,11 +616,6 @@ const refreshAllData = async () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 自动刷新相关
|
|
|
|
|
-const autoRefreshInterval = ref<NodeJS.Timeout | null>(null);
|
|
|
|
|
-const autoRefreshEnabled = ref(false);
|
|
|
|
|
-const autoRefreshSeconds = ref(60); // 默认60秒自动刷新
|
|
|
|
|
-
|
|
|
|
|
// 开启自动刷新
|
|
// 开启自动刷新
|
|
|
const startAutoRefresh = () => {
|
|
const startAutoRefresh = () => {
|
|
|
if (autoRefreshInterval.value) {
|
|
if (autoRefreshInterval.value) {
|
|
@@ -579,10 +624,10 @@ const startAutoRefresh = () => {
|
|
|
|
|
|
|
|
autoRefreshInterval.value = setInterval(() => {
|
|
autoRefreshInterval.value = setInterval(() => {
|
|
|
refreshAllData();
|
|
refreshAllData();
|
|
|
- }, autoRefreshSeconds.value * 1000);
|
|
|
|
|
-
|
|
|
|
|
- autoRefreshEnabled.value = true;
|
|
|
|
|
- ElMessage.success(`已开启自动刷新,每${autoRefreshSeconds.value}秒刷新一次`);
|
|
|
|
|
|
|
+ }, autoRefreshSeconds * 1000);
|
|
|
|
|
+ // 开启倒计时
|
|
|
|
|
+ startCountdown();
|
|
|
|
|
+ ElMessage.success(`已开启自动刷新,每${autoRefreshSeconds}秒刷新一次`);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 停止自动刷新
|
|
// 停止自动刷新
|
|
@@ -591,8 +636,8 @@ const stopAutoRefresh = () => {
|
|
|
clearInterval(autoRefreshInterval.value);
|
|
clearInterval(autoRefreshInterval.value);
|
|
|
autoRefreshInterval.value = null;
|
|
autoRefreshInterval.value = null;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- autoRefreshEnabled.value = false;
|
|
|
|
|
|
|
+ // 停止倒计时
|
|
|
|
|
+ stopCountdown();
|
|
|
ElMessage.info('已停止自动刷新');
|
|
ElMessage.info('已停止自动刷新');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -601,6 +646,8 @@ onUnmounted(() => {
|
|
|
if (autoRefreshInterval.value) {
|
|
if (autoRefreshInterval.value) {
|
|
|
clearInterval(autoRefreshInterval.value);
|
|
clearInterval(autoRefreshInterval.value);
|
|
|
}
|
|
}
|
|
|
|
|
+ // 停止倒计时
|
|
|
|
|
+ stopCountdown();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
@@ -626,6 +673,7 @@ onMounted(async () => {
|
|
|
.ranking-board-page {
|
|
.ranking-board-page {
|
|
|
padding: 20px;
|
|
padding: 20px;
|
|
|
height: calc(100vh - 120px);
|
|
height: calc(100vh - 120px);
|
|
|
|
|
+ position: relative;
|
|
|
}
|
|
}
|
|
|
/* 赛事名称标题样式 */
|
|
/* 赛事名称标题样式 */
|
|
|
.event-title {
|
|
.event-title {
|
|
@@ -980,4 +1028,76 @@ onMounted(async () => {
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
padding: 0 5px;
|
|
padding: 0 5px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 右上角倒计时样式 */
|
|
|
|
|
+.countdown-display {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 20px;
|
|
|
|
|
+ right: 20px;
|
|
|
|
|
+ z-index: 1000;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.countdown-card {
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.95);
|
|
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.countdown-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.countdown-icon {
|
|
|
|
|
+ color: #409eff;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.countdown-text {
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #409eff;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ min-width: 30px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* .stop-btn {
|
|
|
|
|
+ padding: 2px;
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stop-btn:hover {
|
|
|
|
|
+ background-color: #fef0f0;
|
|
|
|
|
+} */
|
|
|
|
|
+
|
|
|
|
|
+@keyframes pulse {
|
|
|
|
|
+ 0% {
|
|
|
|
|
+ transform: scale(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ 50% {
|
|
|
|
|
+ transform: scale(1.05);
|
|
|
|
|
+ }
|
|
|
|
|
+ 100% {
|
|
|
|
|
+ transform: scale(1);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 响应式设计 */
|
|
|
|
|
+@media (max-width: 768px) {
|
|
|
|
|
+ .countdown-display {
|
|
|
|
|
+ top: 10px;
|
|
|
|
|
+ right: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .countdown-content {
|
|
|
|
|
+ padding: 2px 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .countdown-text {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ min-width: 25px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|