From f04f35b562760afbac0c477357e2a29f77aec3b9 Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期四, 02 十月 2025 13:51:47 +0800
Subject: [PATCH] fix: 修复评审次数重复显示问题

---
 web/src/views/review-list.vue |  350 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 343 insertions(+), 7 deletions(-)

diff --git a/web/src/views/review-list.vue b/web/src/views/review-list.vue
index 1df7c38..c63b301 100644
--- a/web/src/views/review-list.vue
+++ b/web/src/views/review-list.vue
@@ -60,18 +60,24 @@
       v-loading="projectsLoading"
       empty-text="璇峰厛閫夋嫨姣旇禌"
     >
-      <el-table-column prop="playerName" label="椤圭洰鍚嶇О" min-width="150">
+      <el-table-column prop="projectName" label="椤圭洰鍚嶇О" min-width="150">
         <template #default="scope">
-          {{ scope.row.projectName || scope.row.playerName }}
+          {{ scope.row.projectName || '鏈~鍐欓」鐩悕绉�' }}
         </template>
       </el-table-column>
       <el-table-column prop="playerName" label="鍙傝禌浜哄鍚�" min-width="120" />
       <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" min-width="120" />
       <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
         <template #default="scope">
-          <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
+          <el-button
+            text
+            :type="scope.row.ratingCount > 0 ? 'success' : 'info'"
+            @click="showRatingList(scope.row)"
+            :disabled="scope.row.ratingCount === 0"
+            class="rating-count-btn"
+          >
             {{ scope.row.ratingCount }}
-          </el-tag>
+          </el-button>
         </template>
       </el-table-column>
       <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center">
@@ -119,6 +125,107 @@
         @current-change="handleCurrentChange"
       />
     </div>
+
+    <!-- 璇勫鍒楄〃寮圭獥 -->
+    <el-dialog
+      v-model="ratingListVisible"
+      title="璇勫鍒楄〃"
+      width="60%"
+      :before-close="handleRatingListClose"
+    >
+      <div v-if="selectedProject">
+        <div class="dialog-header">
+          <h4>{{ selectedProject.projectName || selectedProject.playerName }} - 璇勫璇︽儏</h4>
+          <p class="project-info">鍙傝禌浜猴細{{ selectedProject.playerName }} | 鑱旂郴鐢佃瘽锛歿{ selectedProject.phone }}</p>
+        </div>
+        
+        <el-table 
+          :data="judgeRatings" 
+          v-loading="ratingsLoading"
+          style="width: 100%"
+        >
+          <el-table-column prop="judgeName" label="璇勫濮撳悕" min-width="120" />
+          <el-table-column prop="totalScore" label="璇勫垎" width="100" align="center">
+            <template #default="scope">
+              <span v-if="scope.row.hasRated && scope.row.totalScore" class="score">
+                {{ scope.row.totalScore.toFixed(1) }}
+              </span>
+              <span v-else class="no-score">鏈瘎鍒�</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="hasRated" label="鐘舵��" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.hasRated ? 'success' : 'info'">
+                {{ scope.row.hasRated ? '宸茶瘎鍒�' : '鏈瘎鍒�' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔" width="100" align="center">
+            <template #default="scope">
+              <el-button
+                text
+                type="primary"
+                @click="viewRatingDetail(scope.row)"
+                :disabled="!scope.row.hasRated"
+                size="small"
+              >
+                鏌ョ湅璇︽儏
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="handleRatingListClose">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 璇勫垎璇︽儏寮圭獥 -->
+    <el-dialog
+      v-model="ratingDetailVisible"
+      title="璇勫垎璇︽儏"
+      width="50%"
+      :before-close="handleRatingDetailClose"
+    >
+      <div v-if="selectedRating">
+        <div class="rating-detail-header">
+          <h4>璇勫锛歿{ selectedRating.judgeName }}</h4>
+          <p>鎬诲垎锛�<span class="total-score">{{ selectedRating.totalScore.toFixed(1) }}</span></p>
+        </div>
+        
+        <el-table 
+          :data="ratingItems" 
+          v-loading="ratingDetailLoading"
+          style="width: 100%"
+        >
+          <el-table-column prop="itemName" label="璇勫垎椤圭洰" min-width="150" />
+          <el-table-column prop="score" label="寰楀垎" width="100" align="center">
+            <template #default="scope">
+              <span class="item-score">{{ scope.row.score }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="maxScore" label="婊″垎" width="100" align="center">
+            <template #default="scope">
+              <span class="max-score">{{ scope.row.maxScore || 100 }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        
+        <div v-if="selectedRating.remark" class="rating-comment">
+          <h5>璇勮锛�</h5>
+          <p>{{ selectedRating.remark }}</p>
+        </div>
+      </div>
+      
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="handleRatingDetailClose">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -127,8 +234,10 @@
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import { Search, Trophy, View } from '@element-plus/icons-vue'
-import { getActiveActivities, getRatingStats } from '@/api/projectReview'
+import { getActiveActivities, getRatingStats, getJudgeRatingDetail } from '@/api/projectReview'
 import { getProjectReviewApplications } from '@/api/projectReviewNew'
+import { userApi } from '@/api/user'
+import { getUserInfo } from '@/utils/auth'
 
 const router = useRouter()
 
@@ -139,6 +248,16 @@
 const searchName = ref('')
 const activitiesLoading = ref(false)
 const projectsLoading = ref(false)
+
+// 璇勫寮圭獥鐩稿叧鏁版嵁
+const ratingListVisible = ref(false)
+const ratingDetailVisible = ref(false)
+const selectedProject = ref(null)
+const selectedRating = ref(null)
+const judgeRatings = ref([])
+const ratingItems = ref([])
+const ratingsLoading = ref(false)
+const ratingDetailLoading = ref(false)
 
 // 鍒嗛〉鏁版嵁
 const currentPage = ref(1)
@@ -251,8 +370,147 @@
 }
 
 // 鏌ョ湅璇︽儏
-const viewDetails = (projectId) => {
-  router.push(`/project-review/${projectId}/detail`)
+const viewDetails = async (projectId) => {
+  // 浼犻�抯tageId鍙傛暟锛宻electedActivity.value灏辨槸褰撳墠閫変腑鐨剆tageId
+  const stageId = selectedActivity.value
+  if (!stageId) {
+    ElMessage.warning('璇峰厛閫夋嫨姣旇禌闃舵')
+    return
+  }
+
+  try {
+    // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅
+    const userInfo = getUserInfo()
+    if (!userInfo) {
+      ElMessage.error('鐢ㄦ埛鏈櫥褰曪紝璇烽噸鏂扮櫥褰�')
+      return
+    }
+
+    // 妫�鏌ョ敤鎴锋槸鍚︽湁employee韬唤
+    const hasEmployeeRole = !!userInfo.employee
+    
+    if (hasEmployeeRole) {
+      // 濡傛灉鐢ㄦ埛鏈塭mployee韬唤锛岀洿鎺ュ厑璁告煡鐪嬶紙涓嶉渶瑕佹潈闄愭鏌ワ級
+      console.log('鐢ㄦ埛鍏锋湁鍛樺伐韬唤锛屽厑璁告煡鐪嬫墍鏈夎瘎鍒嗚褰�')
+      router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
+      return
+    }
+
+    // 濡傛灉鐢ㄦ埛鍙湁judge韬唤锛堟病鏈塭mployee韬唤锛夛紝闇�瑕佹鏌ヨ瘎濮旀潈闄�
+    const judgeInfo = await userApi.getCurrentJudgeInfo()
+    
+    if (!judgeInfo) {
+      ElMessage.error('鎮ㄦ病鏈夎瘎濮旀潈闄愶紝鏃犳硶杩涜璇勫')
+      return
+    }
+
+    // 妫�鏌ヨ瘎濮旀槸鍚︽湁褰撳墠姣旇禌闃舵鐨勬潈闄�
+    const hasPermission = await userApi.checkJudgeInActivity(stageId, judgeInfo.judgeId)
+    
+    if (!hasPermission) {
+      ElMessage.error('鎮ㄦ病鏈夊綋鍓嶆瘮璧涢樁娈电殑璇勫鏉冮檺锛屾棤娉曡繘鍏ヨ瘎瀹¢〉闈�')
+      return
+    }
+
+    // 鏉冮檺妫�鏌ラ�氳繃锛岃烦杞埌璇勫椤甸潰
+    router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
+  } catch (error) {
+    console.error('鏉冮檺妫�鏌ュけ璐�:', error)
+    ElMessage.error('鏉冮檺楠岃瘉澶辫触锛岃閲嶆柊鐧诲綍')
+  }
+}
+
+// 鏄剧ず璇勫鍒楄〃
+const showRatingList = async (project) => {
+  if (project.ratingCount === 0) {
+    ElMessage.warning('璇ラ」鐩殏鏃犺瘎瀹¤褰�')
+    return
+  }
+  
+  selectedProject.value = project
+  ratingListVisible.value = true
+  
+  // 鍔犺浇璇勫鍒楄〃
+  await loadJudgeRatings(project.id)
+}
+
+// 鍔犺浇璇勫璇勫垎鍒楄〃
+const loadJudgeRatings = async (activityPlayerId) => {
+  ratingsLoading.value = true
+  try {
+    const result = await getRatingStats(activityPlayerId)
+    console.log('getRatingStats 杩斿洖鏁版嵁:', result)
+    
+    // 浣跨敤姝g‘鐨勫瓧娈靛悕
+    judgeRatings.value = result.ratings || []
+    console.log('judgeRatings 璁剧疆涓�:', judgeRatings.value)
+  } catch (error) {
+    console.error('鍔犺浇璇勫鍒楄〃澶辫触:', error)
+    ElMessage.error('鍔犺浇璇勫鍒楄〃澶辫触')
+    judgeRatings.value = []
+  } finally {
+    ratingsLoading.value = false
+  }
+}
+
+// 鏌ョ湅璇勫垎璇︽儏
+const viewRatingDetail = async (rating) => {
+  if (!rating.hasRated) {
+    ElMessage.warning('璇ヨ瘎濮斿皻鏈瘎鍒�')
+    return
+  }
+  
+  selectedRating.value = rating
+  ratingDetailVisible.value = true
+  
+  // 鍔犺浇璇勫垎鏄庣粏
+  await loadRatingDetail(selectedProject.value.id, rating.judgeId)
+}
+
+// 鍔犺浇璇勫垎鏄庣粏
+const loadRatingDetail = async (activityPlayerId, judgeId) => {
+  ratingDetailLoading.value = true
+  try {
+    console.log('鍔犺浇璇勫垎鏄庣粏锛宎ctivityPlayerId:', activityPlayerId, 'judgeId:', judgeId)
+    
+    const result = await getJudgeRatingDetail(activityPlayerId, judgeId)
+    console.log('璇勫垎鏄庣粏API杩斿洖:', result)
+    
+    if (result && result.items) {
+      ratingItems.value = result.items.map(item => ({
+        itemName: item.ratingItemName,
+        score: item.score,
+        maxScore: item.maxScore || 100
+      }))
+      
+      // 鏇存柊閫変腑璇勫垎鐨勫娉ㄤ俊鎭�
+      if (selectedRating.value) {
+        selectedRating.value.remark = result.remark
+      }
+    } else {
+      ratingItems.value = []
+    }
+  } catch (error) {
+    console.error('鍔犺浇璇勫垎鏄庣粏澶辫触:', error)
+    ElMessage.error('鍔犺浇璇勫垎鏄庣粏澶辫触: ' + (error.message || '鏈煡閿欒'))
+    ratingItems.value = []
+  } finally {
+    ratingDetailLoading.value = false
+  }
+}
+
+// 鍏抽棴璇勫鍒楄〃寮圭獥
+const handleRatingListClose = () => {
+  ratingListVisible.value = false
+  selectedProject.value = null
+  judgeRatings.value = []
+}
+
+// 鍏抽棴璇勫垎璇︽儏寮圭獥
+const handleRatingDetailClose = () => {
+  ratingDetailVisible.value = false
+  selectedRating.value = null
+  ratingItems.value = []
 }
 
 // 鏍煎紡鍖栨棩鏈�
@@ -350,6 +608,84 @@
   line-height: 1.5;
 }
 
+/* 璇勫娆℃暟鎸夐挳鏍峰紡 */
+.rating-count-btn {
+  font-weight: 500;
+}
+
+/* 寮圭獥鏍峰紡 */
+.dialog-header {
+  margin-bottom: 20px;
+  
+  h4 {
+    margin: 0 0 8px 0;
+    color: #303133;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  
+  .project-info {
+    margin: 0;
+    color: #606266;
+    font-size: 14px;
+  }
+}
+
+.rating-detail-header {
+  margin-bottom: 20px;
+  
+  h4 {
+    margin: 0 0 8px 0;
+    color: #303133;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  
+  .total-score {
+    color: #409eff;
+    font-weight: 600;
+    font-size: 18px;
+  }
+}
+
+.rating-comment {
+  margin-top: 20px;
+  padding: 16px;
+  background-color: #f5f7fa;
+  border-radius: 8px;
+  
+  h5 {
+    margin: 0 0 8px 0;
+    color: #303133;
+    font-size: 14px;
+    font-weight: 600;
+  }
+  
+  p {
+    margin: 0;
+    color: #606266;
+    line-height: 1.6;
+  }
+}
+
+.score {
+  color: #67c23a;
+  font-weight: 600;
+}
+
+.no-score {
+  color: #909399;
+}
+
+.item-score {
+  color: #409eff;
+  font-weight: 500;
+}
+
+.max-score {
+  color: #909399;
+}
+
 /* 鎼滅储宸ュ叿鏍� */
 .search-toolbar {
   display: flex;

--
Gitblit v1.8.0