<template>
|
<div class="rating-form">
|
<div class="form-header">
|
<h3>{{ ratingForm.schemeName || '评分表单' }}</h3>
|
<div class="header-info">
|
<div class="judge-info">
|
<el-tag type="primary" size="large">
|
<el-icon><User /></el-icon>
|
当前评委:{{ currentJudgeName || '未知评委' }}
|
</el-tag>
|
</div>
|
<div class="total-score">
|
总分:{{ totalScore }} / {{ ratingForm.totalMaxScore }}
|
</div>
|
</div>
|
</div>
|
|
<!-- 多评委评分状态 -->
|
<div v-if="judgeRatings && judgeRatings.length > 0" class="judge-ratings-status">
|
<h4>评委评分状态</h4>
|
<div class="judge-status-list">
|
<div
|
v-for="judgeRating in judgeRatings"
|
:key="judgeRating.judgeId"
|
class="judge-status-item"
|
:class="{ 'current-judge': judgeRating.isCurrentJudge }"
|
>
|
<div class="judge-name">
|
<el-icon v-if="judgeRating.isCurrentJudge"><Star /></el-icon>
|
{{ judgeRating.judgeName }}
|
</div>
|
<div class="judge-score">
|
<el-tag
|
:type="judgeRating.status === 1 ? 'success' : 'info'"
|
size="small"
|
>
|
{{ judgeRating.status === 1 ? `已评分: ${judgeRating.totalScore}分` : '未评分' }}
|
</el-tag>
|
</div>
|
</div>
|
</div>
|
|
<!-- 平均分显示 -->
|
<div v-if="averageScore !== null" class="average-score">
|
<el-statistic
|
title="当前平均分"
|
:value="averageScore"
|
:precision="2"
|
suffix="分"
|
/>
|
</div>
|
</div>
|
|
<el-form
|
ref="formRef"
|
:model="formData"
|
:rules="rules"
|
label-width="120px"
|
class="rating-form-content"
|
>
|
<!-- 评分项目 -->
|
<div v-if="ratingForm.items && ratingForm.items.length > 0" class="rating-items">
|
<div
|
v-for="item in ratingForm.items"
|
:key="item.id"
|
class="rating-item"
|
>
|
<el-form-item
|
:label="item.name"
|
:prop="`scores.${item.id}`"
|
class="score-item"
|
>
|
<div class="score-input-group">
|
<el-input-number
|
v-model="formData.scores[item.id]"
|
:min="0"
|
:max="item.maxScore"
|
:precision="1"
|
:step="0.5"
|
placeholder="请输入分数"
|
class="score-input"
|
/>
|
<span class="max-score">/ {{ item.maxScore }}</span>
|
</div>
|
<div v-if="item.description" class="item-description">
|
{{ item.description }}
|
</div>
|
</el-form-item>
|
</div>
|
</div>
|
|
<!-- 评语 -->
|
<el-form-item label="评语" prop="comment" class="comment-item">
|
<el-input
|
v-model="formData.comment"
|
type="textarea"
|
:rows="4"
|
placeholder="请输入评语(选填)"
|
maxlength="500"
|
show-word-limit
|
/>
|
</el-form-item>
|
|
<!-- 提交按钮 -->
|
<el-form-item class="submit-item">
|
<el-button
|
type="primary"
|
size="large"
|
@click="handleSubmit"
|
:loading="submitting"
|
class="submit-btn"
|
>
|
提交评分
|
</el-button>
|
<el-button
|
size="large"
|
@click="handleReset"
|
class="reset-btn"
|
>
|
重置
|
</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, watch } from 'vue'
|
import { ElForm, ElFormItem, ElInputNumber, ElInput, ElButton, ElMessage, ElTag, ElIcon, ElStatistic } from 'element-plus'
|
import { User, Star } from '@element-plus/icons-vue'
|
|
const props = defineProps({
|
ratingForm: {
|
type: Object,
|
required: true
|
},
|
activityPlayerId: {
|
type: [String, Number],
|
required: true
|
},
|
// 新增:当前评委信息
|
currentJudgeName: {
|
type: String,
|
default: ''
|
},
|
// 新增:所有评委评分状态
|
judgeRatings: {
|
type: Array,
|
default: () => []
|
},
|
// 新增:平均分
|
averageScore: {
|
type: Number,
|
default: null
|
},
|
// 新增:当前评委的已有评分
|
existingRating: {
|
type: Object,
|
default: null
|
}
|
})
|
|
const emit = defineEmits(['submit'])
|
|
const formRef = ref()
|
const submitting = ref(false)
|
|
// 表单数据
|
const formData = reactive({
|
scores: {},
|
comment: ''
|
})
|
|
// 初始化分数对象
|
const initScores = () => {
|
if (props.ratingForm.items) {
|
props.ratingForm.items.forEach(item => {
|
// 如果有已有评分,则加载已有分数
|
if (props.existingRating && props.existingRating.items) {
|
const existingItem = props.existingRating.items.find(ratingItem => ratingItem.ratingItemId === item.id)
|
formData.scores[item.id] = existingItem ? existingItem.score : null
|
} else {
|
formData.scores[item.id] = null
|
}
|
})
|
}
|
|
// 加载已有评语
|
if (props.existingRating && props.existingRating.remark) {
|
formData.comment = props.existingRating.remark
|
}
|
}
|
|
// 表单验证规则
|
const rules = computed(() => {
|
const scoreRules = {}
|
if (props.ratingForm.items) {
|
props.ratingForm.items.forEach(item => {
|
scoreRules[`scores.${item.id}`] = [
|
{
|
required: true,
|
message: `请输入${item.name}的分数`,
|
trigger: 'blur'
|
},
|
{
|
type: 'number',
|
min: 0,
|
max: item.maxScore,
|
message: `分数应在0-${item.maxScore}之间`,
|
trigger: 'blur'
|
}
|
]
|
})
|
}
|
|
return scoreRules
|
})
|
|
// 计算总分
|
const totalScore = computed(() => {
|
let total = 0
|
Object.values(formData.scores).forEach(score => {
|
if (typeof score === 'number' && !isNaN(score)) {
|
total += score
|
}
|
})
|
return Math.round(total * 10) / 10 // 保留一位小数
|
})
|
|
// 提交评分
|
const handleSubmit = async () => {
|
try {
|
await formRef.value.validate()
|
|
submitting.value = true
|
|
const ratingData = {
|
activityPlayerId: props.activityPlayerId,
|
scores: formData.scores,
|
comment: formData.comment,
|
totalScore: totalScore.value
|
}
|
|
emit('submit', ratingData)
|
|
} catch (error) {
|
console.error('表单验证失败:', error)
|
ElMessage.error('请检查表单填写是否完整')
|
} finally {
|
submitting.value = false
|
}
|
}
|
|
// 重置表单
|
const handleReset = () => {
|
formRef.value.resetFields()
|
initScores()
|
}
|
|
// 监听评分表单变化,重新初始化
|
watch(() => props.ratingForm, () => {
|
initScores()
|
}, { immediate: true })
|
|
// 监听已有评分变化,重新初始化
|
watch(() => props.existingRating, () => {
|
initScores()
|
}, { immediate: true })
|
</script>
|
|
<style scoped>
|
.rating-form {
|
background: white;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.form-header {
|
margin-bottom: 20px;
|
padding-bottom: 16px;
|
border-bottom: 1px solid #EBEEF5;
|
}
|
|
.form-header h3 {
|
margin: 0 0 12px 0;
|
color: #303133;
|
font-size: 18px;
|
font-weight: 600;
|
}
|
|
.header-info {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
flex-wrap: wrap;
|
gap: 12px;
|
}
|
|
.judge-info {
|
display: flex;
|
align-items: center;
|
}
|
|
.total-score {
|
font-size: 16px;
|
font-weight: 600;
|
color: #409EFF;
|
background: #ECF5FF;
|
padding: 8px 16px;
|
border-radius: 20px;
|
}
|
|
.judge-ratings-status {
|
margin-bottom: 24px;
|
padding: 16px;
|
background: #F8F9FA;
|
border-radius: 8px;
|
border: 1px solid #E9ECEF;
|
}
|
|
.judge-ratings-status h4 {
|
margin: 0 0 16px 0;
|
color: #303133;
|
font-size: 16px;
|
font-weight: 600;
|
}
|
|
.judge-status-list {
|
display: grid;
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
gap: 12px;
|
margin-bottom: 16px;
|
}
|
|
.judge-status-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 12px;
|
background: white;
|
border-radius: 6px;
|
border: 1px solid #E4E7ED;
|
transition: all 0.3s ease;
|
}
|
|
.judge-status-item:hover {
|
border-color: #409EFF;
|
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.1);
|
}
|
|
.judge-status-item.current-judge {
|
border-color: #409EFF;
|
background: #ECF5FF;
|
}
|
|
.judge-name {
|
display: flex;
|
align-items: center;
|
gap: 6px;
|
font-weight: 500;
|
color: #303133;
|
}
|
|
.judge-score {
|
flex-shrink: 0;
|
}
|
|
.average-score {
|
text-align: center;
|
padding: 16px;
|
background: white;
|
border-radius: 6px;
|
border: 1px solid #E4E7ED;
|
}
|
|
.rating-items {
|
margin-bottom: 20px;
|
}
|
|
.rating-item {
|
margin-bottom: 20px;
|
padding: 16px;
|
background: #FAFAFA;
|
border-radius: 8px;
|
border-left: 4px solid #409EFF;
|
}
|
|
.score-item :deep(.el-form-item__label) {
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.score-input-group {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
.score-input {
|
width: 120px;
|
}
|
|
.max-score {
|
color: #909399;
|
font-size: 14px;
|
}
|
|
.item-description {
|
margin-top: 8px;
|
color: #606266;
|
font-size: 13px;
|
line-height: 1.4;
|
}
|
|
.comment-item {
|
margin-bottom: 30px;
|
}
|
|
.submit-item {
|
text-align: center;
|
margin-bottom: 0;
|
}
|
|
.submit-btn {
|
width: 120px;
|
margin-right: 16px;
|
}
|
|
.reset-btn {
|
width: 80px;
|
}
|
|
:deep(.el-form-item__label) {
|
font-weight: 500;
|
}
|
|
:deep(.el-textarea__inner) {
|
resize: vertical;
|
}
|
</style>
|