From 8337c34fcc761d07acaad796d10f3e12e9bbe2d1 Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期日, 05 十月 2025 08:56:04 +0800
Subject: [PATCH] feat: 微信项目详情支持阶段评分时间轴
---
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