|
@@ -16,6 +16,7 @@
|
|
|
<div class="ranking-list ranking-list-full">
|
|
|
<div v-for="(item, index) in athleteScoreList" :key="index" class="ranking-item">
|
|
|
<div class="item-content">
|
|
|
+ <span class="item-rank">{{ getRankDisplay(item, index, athleteScoreList) }}</span>
|
|
|
<span class="item-name">{{ item.athleteName }}</span>
|
|
|
<span class="item-team">{{ item.teamName }}</span>
|
|
|
<span class="item-time">{{ item.totalScore }}</span>
|
|
@@ -34,7 +35,7 @@
|
|
|
<div class="ranking-list ranking-list-full">
|
|
|
<div v-for="(item, index) in teamScores" :key="index" class="ranking-item">
|
|
|
<div class="item-content">
|
|
|
- <span class="item-rank">第{{ item.rank }}名</span>
|
|
|
+ <span class="item-rank">{{ getRankDisplay(item, index, teamScores) }}</span>
|
|
|
<span class="item-team-name">{{ item.teamName }}</span>
|
|
|
<span class="item-score">{{ item.score }}分</span>
|
|
|
</div>
|
|
@@ -56,9 +57,17 @@
|
|
|
<div class="ranking-list ranking-list-full">
|
|
|
<div v-for="(item, index) in projectProgress" :key="index" class="ranking-item">
|
|
|
<div class="item-content">
|
|
|
- <span class="item-name">{{ item.name }}</span>
|
|
|
- <span class="item-type">{{ item.type }}</span>
|
|
|
- <span class="item-time">{{ item.time }}</span>
|
|
|
+ <span class="item-name">{{ item.projectName }}</span>
|
|
|
+ <span class="item-type">{{ getProjectTypeText(item.projectType) }} {{ item.classification === '0' ? '个人' : '团体' }}</span>
|
|
|
+ <span class="item-time">{{ formatTime(item.startTime) }}</span>
|
|
|
+ </div>
|
|
|
+ <!-- 如果有组别信息,显示组别详情 -->
|
|
|
+ <div v-if="item.groups && item.groups.length > 0" class="group-details">
|
|
|
+ <div v-for="group in item.groups" :key="group.groupId" class="group-item">
|
|
|
+ <span class="group-name">{{ group.groupName }}</span>
|
|
|
+ <span class="group-time">{{ formatTimeOnly(group.beginTime) }}</span>
|
|
|
+ <span class="group-status" :class="'status-' + group.status">{{ group.statusText }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -73,8 +82,10 @@ import { ref, computed, onMounted } from 'vue';
|
|
|
import { listScoreRanking, listPersonalRanking, listTeamRanking } from '@/api/system/gameEvent/eventRank';
|
|
|
import { listGameScore } from '@/api/system/gameScore';
|
|
|
import { listGameTeam } from '@/api/system/gameTeam';
|
|
|
+import { getProjectProgress } from '@/api/system/gameEvent/projectProgress';
|
|
|
import { useRouter,useRoute } from 'vue-router';
|
|
|
import { GameTeamVO } from '@/api/system/gameTeam/types';
|
|
|
+import { ProjectProgressVo, GroupProgressVo } from '@/api/system/gameEvent/types';
|
|
|
|
|
|
// 定义队伍积分排行榜的数据结构
|
|
|
interface TeamScore {
|
|
@@ -84,27 +95,19 @@ interface TeamScore {
|
|
|
rank: number;
|
|
|
}
|
|
|
|
|
|
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const { game_project_type } = toRefs<any>(proxy?.useDict('game_project_type'));
|
|
|
+
|
|
|
const route = useRoute();
|
|
|
const eventId = route.params.eventId as string;
|
|
|
|
|
|
const athleteScoreList = ref([]);
|
|
|
|
|
|
-const completedTasks = ref(22);
|
|
|
-const totalTasks = ref(33);
|
|
|
-const progressPercentage = computed(() => (completedTasks.value / totalTasks.value) * 100);
|
|
|
-
|
|
|
-const projectProgress = ref([
|
|
|
- { name: '全部', type: '', time: '' },
|
|
|
- { name: '砥砺前行', type: '团体趣味u竞赛', time: '16:24' },
|
|
|
- { name: '同舟共济', type: '团体趣味u竞赛', time: '16:26' },
|
|
|
- { name: '跳绳', type: '个人趣味', time: '13:25' },
|
|
|
- { name: '托球跑', type: '个人趣味', time: '15:08' },
|
|
|
- { name: '100m', type: '田径丙组', time: '10:18' },
|
|
|
- { name: '200m', type: '田径丙组', time: '10:24' },
|
|
|
- { name: '400m', type: '田径丙组', time: '09:26' },
|
|
|
- { name: '800m', type: '田径丙组', time: '10:28' },
|
|
|
- { name: '1500m', type: '田径丙组', time: '10:17' }
|
|
|
-]);
|
|
|
+const completedTasks = ref(0);
|
|
|
+const totalTasks = ref(0);
|
|
|
+const progressPercentage = computed(() => totalTasks.value > 0 ? (completedTasks.value / totalTasks.value) * 100 : 0);
|
|
|
+
|
|
|
+const projectProgress = ref<ProjectProgressVo[]>([]);
|
|
|
|
|
|
const teamScores = ref<TeamScore[]>([]);
|
|
|
|
|
@@ -156,14 +159,155 @@ const loadTeamScores = async () => {
|
|
|
// 按积分从高到低排序
|
|
|
teamScoreList.sort((a, b) => b.score - a.score);
|
|
|
|
|
|
- // 添加排名
|
|
|
- teamScoreList.forEach((team, index) => {
|
|
|
- team.rank = index + 1;
|
|
|
- });
|
|
|
+ // 添加排名(支持并列排名)
|
|
|
+ let currentRank = 1;
|
|
|
+ for (let i = 0; i < teamScoreList.length; i++) {
|
|
|
+ const team = teamScoreList[i];
|
|
|
+ const currentScore = team.score || 0;
|
|
|
+
|
|
|
+ // 如果不是第一个,检查是否与前一个积分相同
|
|
|
+ if (i > 0) {
|
|
|
+ const previousScore = teamScoreList[i - 1].score || 0;
|
|
|
+ if (currentScore !== previousScore) {
|
|
|
+ currentRank = i + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ team.rank = currentRank;
|
|
|
+ }
|
|
|
|
|
|
teamScores.value = teamScoreList;
|
|
|
};
|
|
|
|
|
|
+// 加载项目进度信息
|
|
|
+const loadProjectProgress = async () => {
|
|
|
+ try {
|
|
|
+ const res = await getProjectProgress(eventId);
|
|
|
+ projectProgress.value = res.data || [];
|
|
|
+
|
|
|
+ // 计算已完成和总任务数
|
|
|
+ let completed = 0;
|
|
|
+ let total = 0;
|
|
|
+
|
|
|
+ projectProgress.value.forEach(project => {
|
|
|
+ if (project.groups && project.groups.length > 0) {
|
|
|
+ // 有组别的项目,统计组别
|
|
|
+ project.groups.forEach(group => {
|
|
|
+ total++;
|
|
|
+ if (group.status === '2') { // 已完成
|
|
|
+ completed++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 没有组别的项目,直接统计项目
|
|
|
+ total++;
|
|
|
+ if (project.status === '2') { // 已完成
|
|
|
+ completed++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ completedTasks.value = completed;
|
|
|
+ totalTasks.value = total;
|
|
|
+
|
|
|
+ // 前端也可以做一次排序确保,虽然后端已经排序了
|
|
|
+ // 按完整时间排序(项目时间或最早组别时间)
|
|
|
+ projectProgress.value.sort((a, b) => {
|
|
|
+ const getEarliestTime = (project: ProjectProgressVo) => {
|
|
|
+ if (project.groups && project.groups.length > 0) {
|
|
|
+ // 有组别,找到最早的组别时间
|
|
|
+ const groupTimes = project.groups
|
|
|
+ .map(g => g.beginTime ? new Date(g.beginTime).getTime() : 0)
|
|
|
+ .filter(time => time > 0);
|
|
|
+ if (groupTimes.length > 0) {
|
|
|
+ return Math.min(...groupTimes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 没有组别或组别时间无效,使用项目时间
|
|
|
+ return project.startTime ? new Date(project.startTime).getTime() : 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ const aTime = getEarliestTime(a);
|
|
|
+ const bTime = getEarliestTime(b);
|
|
|
+
|
|
|
+ return aTime - bTime;
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载项目进度失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取项目类型文本
|
|
|
+const getProjectTypeText = (projectType: string) => {
|
|
|
+ if (!game_project_type.value || !projectType) return '未知';
|
|
|
+
|
|
|
+ const typeItem = game_project_type.value.find((item: any) => item.value === projectType);
|
|
|
+ return typeItem ? typeItem.label : '未知';
|
|
|
+};
|
|
|
+
|
|
|
+// 格式化时间显示(包含日期)
|
|
|
+const formatTime = (timeStr: string) => {
|
|
|
+ if (!timeStr) return '-';
|
|
|
+ try {
|
|
|
+ const date = new Date(timeStr);
|
|
|
+ // 检查是否是有效日期
|
|
|
+ if (isNaN(date.getTime())) {
|
|
|
+ return timeStr;
|
|
|
+ }
|
|
|
+ return date.toLocaleString('zh-CN', {
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit',
|
|
|
+ hour: '2-digit',
|
|
|
+ minute: '2-digit',
|
|
|
+ hour12: false
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ return timeStr;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 格式化时间显示(仅时间,用于组别详情)
|
|
|
+const formatTimeOnly = (timeStr: string) => {
|
|
|
+ if (!timeStr) return '-';
|
|
|
+ try {
|
|
|
+ const date = new Date(timeStr);
|
|
|
+ // 检查是否是有效日期
|
|
|
+ if (isNaN(date.getTime())) {
|
|
|
+ return timeStr;
|
|
|
+ }
|
|
|
+ return date.toLocaleTimeString('zh-CN', {
|
|
|
+ hour: '2-digit',
|
|
|
+ minute: '2-digit',
|
|
|
+ hour12: false
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ return timeStr;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取排名显示文本(支持并列排名,排名数值连续)
|
|
|
+const getRankDisplay = (item: any, index: number, list: any[]) => {
|
|
|
+ // 如果项目有rank字段(如团队排名),直接使用
|
|
|
+ if (item.rank !== undefined) {
|
|
|
+ return `第${item.rank}名`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 个人排名逻辑(排名数值连续)
|
|
|
+ // 计算当前项目的实际排名
|
|
|
+ // 排名 = 比当前积分高的不同积分数量 + 1
|
|
|
+ const currentScore = item.totalScore || 0;
|
|
|
+ const higherScores = new Set();
|
|
|
+
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].totalScore > currentScore) {
|
|
|
+ higherScores.add(list[i].totalScore);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const actualRank = higherScores.size + 1;
|
|
|
+ return `第${actualRank}名`;
|
|
|
+};
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
const res = await listScoreRanking(eventId);
|
|
|
// 按照totalScore字段降序排序(分数高的在前面)
|
|
@@ -171,6 +315,9 @@ onMounted(async () => {
|
|
|
|
|
|
// 加载队伍积分排行榜
|
|
|
await loadTeamScores();
|
|
|
+
|
|
|
+ // 加载项目进度信息
|
|
|
+ await loadProjectProgress();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
@@ -242,7 +389,11 @@ onMounted(async () => {
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
|
|
|
-.item-name,
|
|
|
+.item-name {
|
|
|
+ flex: 2;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
.item-team,
|
|
|
.item-time,
|
|
|
.item-type,
|
|
@@ -252,4 +403,53 @@ onMounted(async () => {
|
|
|
flex: 1;
|
|
|
text-align: center;
|
|
|
}
|
|
|
+
|
|
|
+/* 组别详情样式 */
|
|
|
+.group-details {
|
|
|
+ margin-top: 8px;
|
|
|
+ padding-left: 20px;
|
|
|
+ border-left: 2px solid #e0e0e0;
|
|
|
+}
|
|
|
+
|
|
|
+.group-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 4px 0;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.group-name {
|
|
|
+ flex: 3;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.group-time {
|
|
|
+ flex: 2;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.group-status {
|
|
|
+ flex: 1;
|
|
|
+ text-align: center;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 10px;
|
|
|
+ font-size: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-0 {
|
|
|
+ background-color: #f0f0f0;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.status-1 {
|
|
|
+ background-color: #e6f7ff;
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.status-2 {
|
|
|
+ background-color: #f6ffed;
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
</style>
|