286 lines
8.1 KiB
Vue
286 lines
8.1 KiB
Vue
<template>
|
||
<div class="activity-rating-view">
|
||
<div v-if="ratings.length === 0" class="empty-state">
|
||
<el-empty description="暂无评分信息" :image-size="80" />
|
||
</div>
|
||
|
||
<div v-else>
|
||
<!-- 移除统计信息部分 -->
|
||
|
||
<el-table :data="ratings" height="600" border stripe>
|
||
<!-- 基本信息 - 减少宽度 -->
|
||
<el-table-column label="评分人信息" width="160" fixed="left">
|
||
<template #default="scope">
|
||
<div class="user-info-compact">
|
||
<div class="user-name">{{ scope.row.userName || '未知用户' }}</div>
|
||
<div class="user-detail" v-if="scope.row.studentNo">{{ scope.row.studentNo }}</div>
|
||
<div class="user-detail" v-if="scope.row.phoneNumber">{{ scope.row.phoneNumber }}</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<!-- 所属信息 - 减少宽度 -->
|
||
<el-table-column label="所属信息" width="140">
|
||
<template #default="scope">
|
||
<div class="dept-info-compact">
|
||
<div class="dept-detail" v-if="scope.row.deptName">{{ scope.row.deptName }}</div>
|
||
<div class="dept-detail" v-if="scope.row.majorName">{{ scope.row.majorName }}</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<!-- 总评分 - 减少宽度 -->
|
||
<el-table-column label="总评分" width="100" align="center">
|
||
<template #default="scope">
|
||
<div class="rating-display-compact">
|
||
<el-rate :model-value="scope.row.ratingScore" disabled size="small" />
|
||
<div class="rating-number">{{ scope.row.ratingScore }}</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<!-- 各项分数 - 减少宽度 -->
|
||
<el-table-column label="内容" width="70" align="center">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.contentScore" class="score-badge-small">
|
||
{{ scope.row.contentScore }}
|
||
</div>
|
||
<span v-else class="no-score">-</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="组织" width="70" align="center">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.organizationScore" class="score-badge-small">
|
||
{{ scope.row.organizationScore }}
|
||
</div>
|
||
<span v-else class="no-score">-</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="体验" width="70" align="center">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.experienceScore" class="score-badge-small">
|
||
{{ scope.row.experienceScore }}
|
||
</div>
|
||
<span v-else class="no-score">-</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<!-- 评分备注 - 扩大宽度,取消tooltip,直接显示完整内容 -->
|
||
<el-table-column label="评分备注" min-width="300">
|
||
<template #default="scope">
|
||
<div class="rating-comment-full">
|
||
{{ scope.row.ratingComment || '无备注' }}
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<!-- 评分时间 - 简化显示格式,减少宽度 -->
|
||
<el-table-column label="评分时间" width="120" align="center" fixed="right">
|
||
<template #default="scope">
|
||
<div class="rating-time-compact">
|
||
{{ formatDateTimeCompact(scope.row.ratingTime) }}
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted, watch } from 'vue';
|
||
import { getActivityRatings } from '@/api/stgl/activity';
|
||
import { ActivityRatingVO } from '@/api/stgl/activity/types';
|
||
|
||
interface Props {
|
||
activityId: string | number;
|
||
}
|
||
|
||
const props = defineProps<Props>();
|
||
|
||
const ratings = ref<ActivityRatingVO[]>([]);
|
||
const loading = ref(false);
|
||
|
||
// 修复平均评分计算 - 确保数值类型正确转换
|
||
const averageRating = computed(() => {
|
||
if (ratings.value.length === 0) return 0;
|
||
const sum = ratings.value.reduce((total, rating) => {
|
||
// 确保评分是数字类型
|
||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||
return total + (isNaN(score) ? 0 : score);
|
||
}, 0);
|
||
return Math.round((sum / ratings.value.length) * 10) / 10; // 保留一位小数
|
||
});
|
||
|
||
// 计算最高评分
|
||
const maxRating = computed(() => {
|
||
if (ratings.value.length === 0) return 0;
|
||
return Math.max(...ratings.value.map((rating) => {
|
||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||
return isNaN(score) ? 0 : score;
|
||
}));
|
||
});
|
||
|
||
// 计算最低评分
|
||
const minRating = computed(() => {
|
||
if (ratings.value.length === 0) return 0;
|
||
return Math.min(...ratings.value.map((rating) => {
|
||
const score = typeof rating.ratingScore === 'string' ? parseFloat(rating.ratingScore) : rating.ratingScore;
|
||
return isNaN(score) ? 0 : score;
|
||
}));
|
||
});
|
||
|
||
// 简化的日期时间格式化
|
||
const formatDateTimeCompact = (dateStr?: string): string => {
|
||
if (!dateStr) return '';
|
||
const date = new Date(dateStr);
|
||
return date.toLocaleString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
};
|
||
|
||
// 加载评分数据
|
||
const loadRatings = async () => {
|
||
if (!props.activityId) {
|
||
ratings.value = [];
|
||
return;
|
||
}
|
||
|
||
loading.value = true;
|
||
try {
|
||
console.log('开始加载活动评分数据,活动ID:', props.activityId);
|
||
const res = await getActivityRatings(props.activityId);
|
||
ratings.value = res.data || [];
|
||
console.log('成功加载评分数据,活动ID:', props.activityId, '评分数量:', ratings.value.length);
|
||
} catch (error: any) {
|
||
// 如果是404错误,说明后端暂未实现该功能,静默处理
|
||
if (error.response?.status === 404) {
|
||
console.log('活动评分功能暂未实现,活动ID:', props.activityId);
|
||
} else {
|
||
console.error('加载评分数据失败,活动ID:', props.activityId, '错误:', error);
|
||
}
|
||
ratings.value = [];
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 监听 activityId 变化,自动重新加载评分数据
|
||
watch(() => props.activityId, (newId, oldId) => {
|
||
console.log('活动ID发生变化:', oldId, '->', newId);
|
||
if (newId && newId !== oldId) {
|
||
loadRatings();
|
||
}
|
||
}, { immediate: true }); // immediate: true 确保组件挂载时也会执行一次
|
||
|
||
// 移除 onMounted,因为 watch 的 immediate: true 已经覆盖了这个功能
|
||
</script>
|
||
|
||
<style scoped>
|
||
.activity-rating-view {
|
||
padding: 16px;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
}
|
||
|
||
/* 紧凑的评分人信息样式 */
|
||
.user-info-compact {
|
||
padding: 4px 2px;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.user-name {
|
||
font-weight: 600;
|
||
color: #303133;
|
||
font-size: 13px;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.user-detail {
|
||
font-size: 11px;
|
||
color: #606266;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
/* 紧凑的所属信息样式 */
|
||
.dept-info-compact {
|
||
padding: 4px 2px;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.dept-detail {
|
||
font-size: 11px;
|
||
color: #606266;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
/* 紧凑的评分显示样式 */
|
||
.rating-display-compact {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
}
|
||
|
||
.rating-number {
|
||
font-size: 11px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 小号分数徽章样式 */
|
||
.score-badge-small {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 24px;
|
||
height: 24px;
|
||
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
|
||
color: white;
|
||
border-radius: 50%;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.no-score {
|
||
color: #c0c4cc;
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* 完整评分备注样式 */
|
||
.rating-comment-full {
|
||
padding: 8px 12px;
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
color: #303133;
|
||
word-wrap: break-word;
|
||
word-break: break-all;
|
||
white-space: pre-wrap;
|
||
max-height: none;
|
||
}
|
||
|
||
/* 紧凑的时间显示样式 */
|
||
.rating-time-compact {
|
||
font-size: 11px;
|
||
color: #606266;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
/* 表格行高优化 */
|
||
:deep(.el-table .el-table__row) {
|
||
height: auto !important;
|
||
}
|
||
|
||
:deep(.el-table .el-table__cell) {
|
||
padding: 8px 0 !important;
|
||
}
|
||
</style>
|