<template>
|
<div class="rating-container">
|
<div v-if="loading" class="loading">
|
<el-skeleton :rows="8" animated />
|
</div>
|
|
<div v-else-if="detail" class="rating-content">
|
<!-- 左侧面板:学员信息和参赛信息 -->
|
<div class="left-panel">
|
<!-- 学员信息卡片 -->
|
<PlayerInfoCard :player-info="detail.playerInfo" />
|
|
<!-- 参赛信息卡片 -->
|
<div class="submission-card">
|
<h3>参赛信息</h3>
|
<div class="info-item">
|
<label>比赛名称:</label>
|
<span>{{ detail.activityName }}</span>
|
</div>
|
<div class="info-item">
|
<label>项目简介:</label>
|
<p>{{ detail.description || '暂无简介' }}</p>
|
</div>
|
|
<!-- 提交资料 -->
|
<SubmissionFiles :files="detail.submissionFiles" />
|
</div>
|
</div>
|
|
<!-- 右侧面板:评分表单 -->
|
<div class="right-panel">
|
<RatingForm
|
v-if="ratingScheme"
|
:rating-form="ratingScheme"
|
:activity-player-id="detail.id"
|
:current-judge-name="currentJudgeInfo?.name"
|
:judge-ratings="judgeRatings"
|
:average-score="averageScore"
|
:existing-rating="currentJudgeRating"
|
@submit="handleRatingSubmit"
|
/>
|
<div v-else class="no-rating">
|
<el-empty description="该比赛未配置评分模板" />
|
</div>
|
</div>
|
</div>
|
|
<div v-else class="error">
|
<el-empty description="未找到报名信息" />
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from 'vue'
|
import { useRoute } from 'vue-router'
|
import { ElMessage, ElSkeleton, ElEmpty } from 'element-plus'
|
import {
|
getActivityPlayerDetail,
|
saveActivityPlayerRating,
|
getJudgeRatingsForPlayer,
|
getCurrentJudgeRating,
|
getAverageScoreForPlayer,
|
getCurrentJudgeInfo
|
} from '@/api/activityPlayer'
|
import PlayerInfoCard from '@/components/PlayerInfoCard.vue'
|
import SubmissionFiles from '@/components/SubmissionFiles.vue'
|
import RatingForm from '@/components/RatingForm.vue'
|
|
const route = useRoute()
|
const loading = ref(true)
|
const detail = ref(null)
|
const ratingScheme = ref(null)
|
const currentJudgeInfo = ref(null)
|
const judgeRatings = ref([])
|
const currentJudgeRating = ref(null)
|
const averageScore = ref(0)
|
|
const loadDetail = async () => {
|
try {
|
loading.value = true
|
const response = await getActivityPlayerDetail(route.params.id)
|
console.log('API响应:', response)
|
|
// 检查响应数据结构
|
if (!response) {
|
throw new Error('API响应数据为空')
|
}
|
|
if (!response.activityPlayerDetail) {
|
throw new Error('未找到选手详情数据')
|
}
|
|
detail.value = response.activityPlayerDetail
|
|
// 使用从后端获取的真实评分模板数据
|
if (detail.value.ratingForm) {
|
ratingScheme.value = {
|
schemeId: detail.value.ratingForm.schemeId,
|
schemeName: detail.value.ratingForm.schemeName,
|
items: detail.value.ratingForm.items || [],
|
totalMaxScore: detail.value.ratingForm.totalMaxScore || 0
|
}
|
console.log('使用真实评分模板数据:', ratingScheme.value)
|
} else {
|
console.warn('未找到评分模板数据,使用默认模板')
|
// 如果没有评分模板数据,使用默认模板
|
ratingScheme.value = {
|
schemeId: "1",
|
schemeName: "默认评分标准",
|
items: [
|
{ id: "1", name: "综合评分", maxScore: 100, orderNo: 1 }
|
],
|
totalMaxScore: 100
|
}
|
}
|
|
// 获取当前评委信息
|
try {
|
const judgeInfoResponse = await getCurrentJudgeInfo()
|
console.log('评委信息响应:', judgeInfoResponse)
|
if (judgeInfoResponse && judgeInfoResponse.currentJudgeInfo) {
|
currentJudgeInfo.value = judgeInfoResponse.currentJudgeInfo
|
}
|
} catch (error) {
|
console.error('获取评委信息失败:', error)
|
}
|
|
// 获取所有评委评分状态
|
try {
|
const judgeRatingsResponse = await getJudgeRatingsForPlayer(route.params.id)
|
console.log('评委评分状态响应:', judgeRatingsResponse)
|
if (judgeRatingsResponse && judgeRatingsResponse.judgeRatingsForPlayer) {
|
judgeRatings.value = judgeRatingsResponse.judgeRatingsForPlayer
|
}
|
} catch (error) {
|
console.error('获取评委评分状态失败:', error)
|
}
|
|
// 获取当前评委的评分
|
try {
|
const currentRatingResponse = await getCurrentJudgeRating(route.params.id)
|
console.log('当前评委评分响应:', currentRatingResponse)
|
if (currentRatingResponse && currentRatingResponse.currentJudgeRating) {
|
currentJudgeRating.value = currentRatingResponse.currentJudgeRating
|
}
|
} catch (error) {
|
console.error('获取当前评委评分失败:', error)
|
}
|
|
// 获取平均分
|
try {
|
const averageScoreResponse = await getAverageScoreForPlayer(route.params.id)
|
console.log('平均分响应:', averageScoreResponse)
|
if (averageScoreResponse && averageScoreResponse.averageScoreForPlayer !== undefined) {
|
averageScore.value = averageScoreResponse.averageScoreForPlayer
|
}
|
} catch (error) {
|
console.error('获取平均分失败:', error)
|
}
|
} catch (error) {
|
console.error('加载详情失败:', error)
|
ElMessage.error('加载详情失败')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
const handleRatingSubmit = async (ratingData) => {
|
try {
|
console.log('评分提交:', ratingData)
|
|
// 构造提交数据 - 将 scores 对象转换为 ratings 数组
|
const ratings = Object.entries(ratingData.scores || {}).map(([itemId, score]) => ({
|
itemId: parseInt(itemId),
|
score: score
|
}))
|
|
const input = {
|
activityPlayerId: detail.value.id,
|
ratings: ratings,
|
comment: ratingData.comment || ''
|
}
|
|
// 调用保存 API
|
const result = await saveActivityPlayerRating(input)
|
|
if (result.saveActivityPlayerRating) {
|
ElMessage.success('评分提交成功')
|
|
// 重新加载数据以更新评分状态
|
await loadDetail()
|
} else {
|
ElMessage.error('评分提交失败')
|
}
|
} catch (error) {
|
console.error('评分提交失败:', error)
|
ElMessage.error('评分提交失败: ' + error.message)
|
}
|
}
|
|
onMounted(() => {
|
loadDetail()
|
})
|
</script>
|
|
<style scoped>
|
.rating-container {
|
padding: 20px;
|
min-height: calc(100vh - 120px);
|
}
|
|
.loading {
|
padding: 20px;
|
}
|
|
.rating-content {
|
display: flex;
|
gap: 20px;
|
max-width: 1400px;
|
margin: 0 auto;
|
}
|
|
.left-panel {
|
flex: 1;
|
max-width: 600px;
|
}
|
|
.right-panel {
|
flex: 1;
|
max-width: 600px;
|
}
|
|
.submission-card {
|
background: white;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
margin-top: 20px;
|
}
|
|
.submission-card h3 {
|
margin: 0 0 16px 0;
|
color: #303133;
|
font-size: 18px;
|
font-weight: 600;
|
}
|
|
.info-item {
|
margin-bottom: 12px;
|
}
|
|
.info-item label {
|
font-weight: 600;
|
color: #606266;
|
margin-right: 8px;
|
}
|
|
.info-item p {
|
margin: 4px 0 0 0;
|
color: #303133;
|
line-height: 1.6;
|
}
|
|
.no-rating {
|
background: white;
|
border-radius: 8px;
|
padding: 40px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
text-align: center;
|
}
|
|
.error {
|
text-align: center;
|
padding: 40px;
|
}
|
|
@media (max-width: 1200px) {
|
.rating-content {
|
flex-direction: column;
|
}
|
|
.left-panel,
|
.right-panel {
|
max-width: none;
|
}
|
}
|
</style>
|