fix: 活动评审弹窗总分按维度权重加权计算

Made-with: Cursor
This commit is contained in:
zhonghua 2026-04-15 13:45:29 +08:00
parent e054895c81
commit 6c3b2f4a1d

View File

@ -1,13 +1,6 @@
<template>
<a-drawer
:open="open"
:title="drawerTitle"
placement="right"
:width="1100"
:closable="true"
:destroy-on-close="true"
@close="handleClose"
>
<a-drawer :open="open" :title="drawerTitle" placement="right" :width="1100" :closable="true" :destroy-on-close="true"
@close="handleClose">
<a-spin :spinning="loading">
<div class="review-drawer-content">
<!-- 左侧作品信息 -->
@ -35,28 +28,15 @@
<div class="section">
<div class="section-label">作品详情</div>
<div class="work-tabs" v-if="modelItems.length > 1">
<a-button
v-for="(item, index) in modelItems"
:key="index"
:type="currentFileIndex === index ? 'primary' : 'default'"
size="small"
@click="currentFileIndex = index"
>
<a-button v-for="(item, index) in modelItems" :key="index"
:type="currentFileIndex === index ? 'primary' : 'default'" size="small"
@click="currentFileIndex = index">
作品{{ index + 1 }}
</a-button>
</div>
<!-- 预览区域 -->
<div
class="preview-area"
@mouseenter="isHovering = true"
@mouseleave="isHovering = false"
>
<img
v-if="currentPreviewImage"
:src="currentPreviewImage"
alt="作品预览"
class="preview-image"
/>
<div class="preview-area" @mouseenter="isHovering = true" @mouseleave="isHovering = false">
<img v-if="currentPreviewImage" :src="currentPreviewImage" alt="作品预览" class="preview-image" />
<div v-else class="preview-placeholder">
<FileImageOutlined />
<span>暂无预览</span>
@ -65,7 +45,9 @@
<transition name="fade">
<div v-show="isHovering && currentModelUrl" class="preview-overlay">
<a-button type="primary" @click="handlePreview3DModel">
<template #icon><EyeOutlined /></template>
<template #icon>
<EyeOutlined />
</template>
3D预览
</a-button>
</div>
@ -81,20 +63,12 @@
<div class="section" v-if="workDetail?.attachments?.length">
<div class="section-label">作品附件</div>
<div class="attachments-list">
<div
v-for="attachment in workDetail.attachments"
:key="attachment.id"
class="attachment-group"
>
<div v-for="attachment in workDetail.attachments" :key="attachment.id" class="attachment-group">
<div class="attachment-name">附件名称{{ getAttachmentCategory(attachment) }}</div>
<div class="attachment-item">
<PaperClipOutlined class="attachment-icon" />
<span class="file-name">{{ attachment.fileName }}</span>
<a-button
type="link"
size="small"
@click="downloadAttachment(attachment)"
>
<a-button type="link" size="small" @click="downloadAttachment(attachment)">
<DownloadOutlined />
</a-button>
</div>
@ -117,161 +91,98 @@
</div>
<template v-else>
<!-- 评分标准说明有评审规则时显示 -->
<div class="scoring-standard" v-if="reviewRule">
<div class="standard-title">评分标准</div>
<div class="standard-content">
<div
v-for="(dim, index) in reviewRule.dimensions"
:key="index"
class="dimension-desc"
>
<span class="dim-name">{{ dim.name }}{{ dim.percentage }}</span>
<span class="dim-desc">{{ dim.description || '无说明' }}</span>
</div>
</div>
</div>
<!-- 已评审显示保存的维度评分数据 -->
<template v-if="isReviewed && savedDimensionScores.length > 0">
<div class="dimension-scores">
<div
v-for="(dim, index) in savedDimensionScores"
:key="index"
class="dimension-item"
>
<div class="dimension-label">{{ dim.name }}满分{{ dim.maxScore }}</div>
<div class="dimension-input">
<a-input-number
:value="dim.score"
:disabled="true"
size="small"
class="score-input"
/>
<span class="score-unit"></span>
<!-- 评分标准说明有评审规则时显示 -->
<div class="scoring-standard" v-if="reviewRule">
<div class="standard-title">评分标准</div>
<div class="standard-content">
<div v-for="(dim, index) in reviewRule.dimensions" :key="index" class="dimension-desc">
<span class="dim-name">{{ dim.name }}{{ dim.percentage }}%</span>
<span class="dim-desc">{{ dim.description || '无说明' }}</span>
</div>
</div>
</div>
</template>
<!-- 未评审显示当前评审规则的维度评分输入 -->
<template v-else-if="reviewRule && !isReviewed">
<!-- 各维度评分 -->
<div class="dimension-scores">
<div
v-for="(dim, index) in reviewRule.dimensions"
:key="index"
class="dimension-item"
>
<div class="dimension-label">{{ dim.name }}满分{{ dim.percentage }}</div>
<div class="dimension-input">
<a-button
size="small"
@click="decreaseScore(index)"
>-</a-button>
<a-input-number
v-model:value="dimensionScores[index]"
:min="0"
:max="dim.percentage"
:precision="0"
size="small"
class="score-input"
/>
<span class="score-unit"></span>
<a-button
size="small"
@click="increaseScore(index, dim.percentage)"
>+</a-button>
<!-- 已评审显示保存的维度评分数据 -->
<template v-if="isReviewed && savedDimensionScores.length > 0">
<div class="dimension-scores">
<div v-for="(dim, index) in savedDimensionScores" :key="index" class="dimension-item">
<div class="dimension-label">{{ dim.name }}权重{{ dim.maxScore }}%</div>
<div class="dimension-input">
<a-input-number :value="dim.score" :disabled="true" size="small" class="score-input" />
<span class="score-unit"></span>
</div>
</div>
</div>
</template>
<!-- 未评审显示当前评审规则的维度评分输入 -->
<template v-else-if="reviewRule && !isReviewed">
<!-- 各维度评分 -->
<div class="dimension-scores">
<div v-for="(dim, index) in reviewRule.dimensions" :key="index" class="dimension-item">
<div class="dimension-label">{{ dim.name }}权重{{ dim.percentage }}%</div>
<div class="dimension-input">
<a-button size="small" @click="decreaseScore(index)">-</a-button>
<a-input-number v-model:value="dimensionScores[index]" :min="0" :max="100" :precision="0"
size="small" class="score-input" />
<span class="score-unit"></span>
<a-button size="small" @click="increaseScore(index, 100)">+</a-button>
</div>
</div>
</div>
</template>
<!-- 简单评分模式无评审规则时 -->
<div class="simple-score" v-else>
<div class="dimension-label">评分满分100分</div>
<div class="dimension-input">
<a-input-number v-model:value="simpleScore" :min="0" :max="100" :precision="2" :disabled="isReviewed"
style="width: 150px" />
<span class="score-unit"></span>
</div>
</div>
</template>
<!-- 简单评分模式无评审规则时 -->
<div class="simple-score" v-else>
<div class="dimension-label">评分满分100分</div>
<div class="dimension-input">
<a-input-number
v-model:value="simpleScore"
:min="0"
:max="100"
:precision="2"
:disabled="isReviewed"
style="width: 150px"
/>
<span class="score-unit"></span>
<!-- 总得分 -->
<div class="total-score">
<span class="total-label">总得分</span>
<span class="total-value">{{ totalScore }}</span>
<span class="total-unit"></span>
</div>
</div>
<!-- 总得分 -->
<div class="total-score">
<span class="total-label">总得分</span>
<span class="total-value">{{ totalScore }}</span>
<span class="total-unit"></span>
</div>
<!-- 老师点评 -->
<div class="comments-section">
<div class="comments-title">老师点评</div>
<a-select v-model:value="selectedPresetComment" placeholder="选择预设点评"
style="width: 100%; margin-bottom: 12px" :disabled="isReviewed" allow-clear
@change="handlePresetSelect">
<a-select-option v-for="preset in presetComments" :key="preset.id" :value="preset.id">
{{ preset.content }}
</a-select-option>
</a-select>
<a-textarea v-model:value="comments" placeholder="请输入评语或选择预设评语" :rows="4" :maxlength="500" show-count
:disabled="isReviewed" />
</div>
<!-- 老师点评 -->
<div class="comments-section">
<div class="comments-title">老师点评</div>
<a-select
v-model:value="selectedPresetComment"
placeholder="选择预设点评"
style="width: 100%; margin-bottom: 12px"
:disabled="isReviewed"
allow-clear
@change="handlePresetSelect"
>
<a-select-option
v-for="preset in presetComments"
:key="preset.id"
:value="preset.id"
>
{{ preset.content }}
</a-select-option>
</a-select>
<a-textarea
v-model:value="comments"
placeholder="请输入评语或选择预设评语"
:rows="4"
:maxlength="500"
show-count
:disabled="isReviewed"
/>
</div>
<!-- 操作按钮 -->
<div class="action-buttons" v-if="!isReviewed">
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
提交
</a-button>
<a-button @click="handleReset">重置</a-button>
<a-button danger @click="handleViolation">违规</a-button>
</div>
<!-- 操作按钮 -->
<div class="action-buttons" v-if="!isReviewed">
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
提交
</a-button>
<a-button @click="handleReset">重置</a-button>
<a-button danger @click="handleViolation">违规</a-button>
</div>
</template>
</div>
</div>
</div>
</a-spin>
<a-modal
v-model:open="violationModalOpen"
title="标记违规"
ok-text="确定"
cancel-text="取消"
:confirm-loading="violationSubmitting"
destroy-on-close
@ok="handleViolationConfirm"
>
<a-modal v-model:open="violationModalOpen" title="标记违规" ok-text="确定" cancel-text="取消"
:confirm-loading="violationSubmitting" destroy-on-close @ok="handleViolationConfirm">
<p class="violation-modal-hint">
确定标记后作品状态将变为违规并结束您在本作品上的评审任务
</p>
<a-textarea
v-model:value="violationReason"
placeholder="可选:违规说明"
:rows="3"
:maxlength="500"
show-count
/>
<a-textarea v-model:value="violationReason" placeholder="可选:违规说明" :rows="3" :maxlength="500" show-count />
</a-modal>
</a-drawer>
</template>
@ -407,17 +318,27 @@ const currentPreviewImage = computed(() => {
return item?.previewUrl || workDetail.value?.previewUrl || null;
});
//
// 0100 = Σ( × % / 100)
const totalScore = computed(() => {
//
if (isReviewed.value && existingScore.value?.totalScore !== null) {
if (
isReviewed.value &&
existingScore.value?.totalScore !== null &&
existingScore.value?.totalScore !== undefined
) {
return Number(existingScore.value.totalScore);
}
//
if (reviewRule.value) {
return dimensionScores.value.reduce((sum, score) => sum + (score || 0), 0);
if (reviewRule.value?.dimensions?.length) {
let sum = 0;
for (let i = 0; i < reviewRule.value.dimensions.length; i++) {
const dim = reviewRule.value.dimensions[i];
const s = Number(dimensionScores.value[i] ?? 0);
const w = Number(dim.percentage ?? 0) / 100;
sum += s * w;
}
return Math.round(sum * 100) / 100;
}
return simpleScore.value || 0;
return Number(simpleScore.value ?? 0);
});
//
@ -445,7 +366,7 @@ const downloadAttachment = (attachment: any) => {
document.body.removeChild(link);
};
//
// 0100
const increaseScore = (index: number, maxScore: number) => {
const current = dimensionScores.value[index] || 0;
if (current < maxScore) {
@ -468,7 +389,7 @@ const handlePresetSelect = (presetId: number | undefined) => {
if (preset) {
comments.value = preset.content;
// 使
presetCommentsApi.incrementUseCount(presetId).catch(() => {});
presetCommentsApi.incrementUseCount(presetId).catch(() => { });
}
}
};
@ -594,12 +515,12 @@ const handleSubmit = async () => {
//
if (reviewRule.value) {
const hasInvalidScore = dimensionScores.value.some((score, idx) => {
const dim = reviewRule.value!.dimensions[idx];
return score === undefined || score === null || score < 0 || score > dim.percentage;
const hasInvalidScore = dimensionScores.value.some((score) => {
const s = score === undefined || score === null ? NaN : Number(score);
return Number.isNaN(s) || s < 0 || s > 100;
});
if (hasInvalidScore) {
message.warning("请为所有评分维度填写有效分数");
message.warning("请为所有评分维度填写 0100 的有效分数");
return;
}
} else {
@ -622,7 +543,8 @@ const handleSubmit = async () => {
if (reviewRule.value?.dimensions) {
scoreData.dimensionScores = reviewRule.value.dimensions.map((dim, idx) => ({
name: dim.name,
score: dimensionScores.value[idx] || 0,
score: dimensionScores.value[idx] ?? 0,
/** 权重占比(%),总得分由前端按 Σ(score×percentage/100) 计算后写入 totalScore */
maxScore: dim.percentage,
}));
}
@ -948,7 +870,7 @@ $primary: #0958d9;
gap: 8px;
.score-input {
width: 60px;
width: 100px;
text-align: center;
}