From afeeed281e60466b576fbe74d339634cc5d07b82 Mon Sep 17 00:00:00 2001 From: Codex Assistant <codex@example.com> Date: 星期三, 08 十月 2025 08:56:42 +0800 Subject: [PATCH] 修复评审功能和用户认证问题 --- web/src/views/review-list.vue | 464 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 349 insertions(+), 115 deletions(-) diff --git a/web/src/views/review-list.vue b/web/src/views/review-list.vue index 1df7c38..932877b 100644 --- a/web/src/views/review-list.vue +++ b/web/src/views/review-list.vue @@ -21,10 +21,25 @@ clearable /> <el-select + v-model="selectedRegion" + placeholder="璇烽�夋嫨鍖哄煙" + @change="handleRegionChange" + style="width: 200px" + clearable + filterable + > + <el-option + v-for="region in regions" + :key="region.id" + :label="region.name" + :value="region.id" + /> + </el-select> + <el-select v-model="selectedActivity" placeholder="璇烽�夋嫨姣旇禌" @change="handleActivityChange" - style="width: 200px" + style="width: 220px" clearable filterable > @@ -43,9 +58,9 @@ </span> </el-option> </el-select> - <el-button - type="primary" - :icon="Search" + <el-button + type="primary" + :icon="Search" @click="loadProjects" :loading="projectsLoading" > @@ -54,24 +69,30 @@ </div> </div> - <el-table - :data="projects" - style="width: 100%" + <el-table + :data="projects" + style="width: 100%" 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,31 +140,172 @@ @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="ratingDetail"> + <div class="dialog-header"> + <h4>{{ ratingDetail.judgeName }} 璇勫缁撴灉</h4> + <p class="project-info"> + 鎬诲垎锛� + <span class="total-score"> + {{ ratingDetail.totalScore?.toFixed(1) ?? '鏆傛棤' }} + </span> + </p> + </div> + + <el-table + :data="ratingDetail.items || []" + border + size="small" + > + <el-table-column prop="itemName" label="璇勫垎椤�" min-width="160" /> + <el-table-column prop="score" label="寰楀垎" width="100" align="center"> + <template #default="scope"> + <span class="item-score">{{ scope.row.score?.toFixed(1) ?? '-' }}</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?.toFixed(1) ?? '-' }}</span> + </template> + </el-table-column> + </el-table> + + <div class="rating-comment" v-if="ratingDetail.comment"> + <h5>璇勮</h5> + <p>{{ ratingDetail.comment }}</p> + </div> + </div> + + <template #footer> + <div class="dialog-footer"> + <el-button @click="handleRatingDetailClose">鍏抽棴</el-button> + </div> + </template> + </el-dialog> </div> </template> -<script setup> +<script setup lang="ts"> import { ref, onMounted } from 'vue' 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 { getLeafRegions } from '@/api/region' +import { userApi } from '@/api/user' +import { getUserInfo } from '@/utils/auth' const router = useRouter() -// 鍝嶅簲寮忔暟鎹� -const activities = ref([]) -const selectedActivity = ref(null) -const projects = ref([]) +interface ActivityItem { + id: number + name: string + pid: number + parent?: ActivityItem +} + +interface RegionItem { + id: number + name: string +} + +const activities = ref<ActivityItem[]>([]) +const regions = ref<RegionItem[]>([]) +const selectedActivity = ref<number | null>(null) +const selectedRegion = ref<number | null>(null) +const projects = ref<any[]>([]) const searchName = ref('') const activitiesLoading = ref(false) const projectsLoading = ref(false) -// 鍒嗛〉鏁版嵁 +// 寮圭獥鐩稿叧鐘舵�� +const ratingListVisible = ref(false) +const ratingDetailVisible = ref(false) +const selectedProject = ref<any | null>(null) +const judgeRatings = ref<any[]>([]) +const ratingDetail = ref<any | null>(null) +const ratingsLoading = ref(false) +const ratingDetailLoading = ref(false) + +// 鍒嗛〉 const currentPage = ref(1) const pageSize = ref(10) const total = ref(0) + +const loadRegions = async () => { + try { + const data = await getLeafRegions() + regions.value = data || [] + } catch (error: any) { + console.error('鍔犺浇鍖哄煙鍒楄〃澶辫触:', error) + ElMessage.error(error?.message || '鍔犺浇鍖哄煙鍒楄〃澶辫触') + } +} // 鍔犺浇姣旇禌鍒楄〃 const loadActivities = async () => { @@ -152,16 +314,20 @@ try { console.log('璋冪敤 getActiveActivities...') const data = await getActiveActivities() - console.log('getActiveActivities 杩斿洖鏁版嵁:', data) - activities.value = data - console.log('activities.value 璁剧疆涓�:', activities.value) - console.log('activities.value.length:', activities.value?.length) - } catch (error) { + console.log('姣旇禌鏁版嵁锛�', data) + + activities.value = data || [] + + if (activities.value.length === 1) { + selectedActivity.value = activities.value[0].id + console.log('鑷姩閫変腑鍞竴姣旇禌:', selectedActivity.value) + loadProjects() + } + } catch (error: any) { console.error('鍔犺浇姣旇禌鍒楄〃澶辫触:', error) - ElMessage.error(error.message) + ElMessage.error(error?.message || '鍔犺浇姣旇禌鍒楄〃澶辫触') } finally { activitiesLoading.value = false - console.log('=== 姣旇禌鍒楄〃鍔犺浇瀹屾垚 ===') } } @@ -171,51 +337,54 @@ ElMessage.warning('璇峰厛閫夋嫨姣旇禌') return } - + console.log('=== 寮�濮嬪姞杞介」鐩垪琛� ===') console.log('selectedActivity.value:', selectedActivity.value) + console.log('selectedRegion.value:', selectedRegion.value) console.log('currentPage.value:', currentPage.value) console.log('pageSize.value:', pageSize.value) - + projectsLoading.value = true try { - const params = { + const params: Record<string, unknown> = { activityId: selectedActivity.value, page: currentPage.value, size: pageSize.value } - - // 濡傛灉鏈夋悳绱㈠悕绉帮紝娣诲姞鍒板弬鏁颁腑 + + if (selectedRegion.value) { + params.regionId = selectedRegion.value + } + if (searchName.value && searchName.value.trim()) { params.name = searchName.value.trim() } - - console.log('API璋冪敤鍙傛暟:', params) - + + console.log('璇锋眰鍙傛暟:', params) const response = await getProjectReviewApplications(params) console.log('API鍝嶅簲:', response) - - // 澶勭悊鍝嶅簲鏁版嵁 - 鏂扮殑鍒嗛〉缁撴瀯 + const pageData = response.projectReviewApplications projects.value = pageData?.content || [] total.value = pageData?.totalElements || 0 - - console.log('璁剧疆 projects.value:', projects.value) - console.log('projects.value.length:', projects.value.length) - console.log('璁剧疆 total.value:', total.value) - } catch (error) { + } catch (error: any) { console.error('鍔犺浇椤圭洰鍒楄〃澶辫触:', error) - ElMessage.error('鍔犺浇椤圭洰鍒楄〃澶辫触') - // 濡傛灉API璋冪敤澶辫触锛屾樉绀虹┖鏁版嵁 - projects.value = [] - total.value = 0 + ElMessage.error(error?.message || '鍔犺浇椤圭洰鍒楄〃澶辫触') } finally { projectsLoading.value = false } } +// 澶勭悊鍖哄煙閫夋嫨鍙樺寲 +const handleRegionChange = () => { + currentPage.value = 1 + if (selectedActivity.value) { + loadProjects() + } +} + // 澶勭悊姣旇禌閫夋嫨鍙樺寲 -const handleActivityChange = (activityId) => { +const handleActivityChange = () => { currentPage.value = 1 loadProjects() } @@ -223,78 +392,86 @@ // 娓呯┖鎼滅储 const handleClear = () => { searchName.value = '' - currentPage.value = 1 - if (selectedActivity.value) { - loadProjects() - } + loadProjects() } -// 閲嶇疆鎼滅储 -const resetSearch = () => { - searchName.value = '' - selectedActivity.value = null - currentPage.value = 1 - projects.value = [] - total.value = 0 -} - -// 鍒嗛〉澶勭悊 -const handleSizeChange = (size) => { +// 鍒嗛〉澶у皬鏀瑰彉 +const handleSizeChange = (size: number) => { pageSize.value = size currentPage.value = 1 loadProjects() } -const handleCurrentChange = (page) => { +// 褰撳墠椤垫敼鍙� +const handleCurrentChange = (page: number) => { currentPage.value = page loadProjects() } -// 鏌ョ湅璇︽儏 -const viewDetails = (projectId) => { - router.push(`/project-review/${projectId}/detail`) -} - -// 鏍煎紡鍖栨棩鏈� -const formatDate = (dateString) => { - if (!dateString) return '-' - return new Date(dateString).toLocaleString('zh-CN') -} - -// 鑾峰彇鐘舵�佺被鍨� -const getStateType = (state) => { - const stateMap = { - 0: 'danger', // 宸叉嫆缁� - 1: 'warning', // 寰呭鏍� - 2: 'success', // 宸查�氳繃 - 3: 'info' // 宸茬粨鏉� +// 灞曠ず璇勫鍒楄〃 +const showRatingList = async (project: any) => { + selectedProject.value = project + ratingsLoading.value = true + ratingListVisible.value = true + try { + const data = await getRatingStats(project.id) + judgeRatings.value = data || [] + } catch (error: any) { + console.error('鍔犺浇璇勫鍒楄〃澶辫触:', error) + ElMessage.error(error?.message || '鍔犺浇璇勫鍒楄〃澶辫触') + } finally { + ratingsLoading.value = false } - return stateMap[state] || 'info' } -// 鑾峰彇鐘舵�佸悕绉� -const getStateName = (state) => { - const stateMap = { - 0: '宸叉嫆缁�', - 1: '寰呰瘎瀹�', - 2: '宸查�氳繃', - 3: '宸茬粨鏉�' +const handleRatingListClose = () => { + ratingListVisible.value = false + judgeRatings.value = [] +} + +// 鏌ョ湅璇勫垎璇︽儏 +const viewRatingDetail = async (rating: any) => { + ratingDetailVisible.value = true + ratingDetailLoading.value = true + try { + const data = await getJudgeRatingDetail(rating.id) + ratingDetail.value = data + } catch (error: any) { + console.error('鍔犺浇璇勫垎璇︽儏澶辫触:', error) + ElMessage.error(error?.message || '鍔犺浇璇勫垎璇︽儏澶辫触') + } finally { + ratingDetailLoading.value = false } - return stateMap[state] || '鏈煡' +} + +const handleRatingDetailClose = () => { + ratingDetailVisible.value = false + ratingDetail.value = null +} + +// 鏍煎紡鍖栨椂闂� +const formatDate = (value: string | number | Date) => { + if (!value) return '-' + const date = new Date(value) + return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String( + date.getDate() + ).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String( + date.getMinutes() + ).padStart(2, '0')}` } // 鑾峰彇璇勫鐘舵�佺被鍨嬶紙鍩轰簬璇勫娆℃暟锛� -const getReviewStatusType = (ratingCount) => { +const getReviewStatusType = (ratingCount: number) => { return ratingCount > 0 ? 'success' : 'warning' } // 鑾峰彇璇勫鐘舵�佸悕绉帮紙鍩轰簬璇勫娆℃暟锛� -const getReviewStatusName = (ratingCount) => { +const getReviewStatusName = (ratingCount: number) => { return ratingCount > 0 ? '宸茶瘎瀹�' : '鏈瘎瀹�' } // 鑾峰彇姣旇禌鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥炵埗姣旇禌鍚嶇О锛涘鏋滄槸姣旇禌锛岃繑鍥炶嚜宸辩殑鍚嶇О锛� -const getActivityName = (activity) => { +const getActivityName = (activity: ActivityItem) => { if (activity.pid > 0 && activity.parent) { return activity.parent.name } @@ -302,22 +479,30 @@ } // 鑾峰彇娲诲姩鏄剧ず鍚嶇О锛堢敤浜庢悳绱㈠拰閫変腑鏃舵樉绀猴級 -const getActivityDisplayName = (activity) => { +const getActivityDisplayName = (activity: ActivityItem) => { if (activity.pid > 0 && activity.parent) { return `${activity.parent.name} - ${activity.name}` } return activity.name } -// 鑾峰彇闃舵鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥為樁娈靛悕绉帮紱濡傛灉鏄瘮璧涳紝杩斿洖姣旇禌鍚嶇О锛� -const getStageName = (activity) => { - return activity.name +// 鏉冮檺妫�鏌ワ細鍙湁绠$悊鍛樺彲浠ユ煡鐪嬮」鐩瘎瀹� +const checkPermission = async () => { + const userInfo = getUserInfo() + if (!userInfo) { + router.push('/login') + } } - +// 鏌ョ湅椤圭洰璇︽儏 +const viewDetails = (projectId: number) => { + router.push(`/project-review/${projectId}/detail`) +} // 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹� onMounted(() => { + checkPermission() + loadRegions() loadActivities() }) </script> @@ -350,6 +535,71 @@ line-height: 1.5; } +/* 璇勫娆℃暟鎸夐挳鏍峰紡 */ +.rating-count-btn { + font-weight: 500; +} + +/* 寮圭獥鏍峰紡 */ +.dialog-header { + margin-bottom: 20px; + + h4 { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 600; + color: #1f2937; + } + + .project-info { + margin: 0; + color: #6b7280; + font-size: 13px; + } +} + +.dialog-footer { + text-align: right; +} + +.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; @@ -361,12 +611,6 @@ display: flex; gap: 12px; align-items: center; -} - -.search-area { - display: flex; - align-items: center; - gap: 12px; } /* 鎿嶄綔鎸夐挳鏍峰紡 */ @@ -384,16 +628,6 @@ .view-btn:hover { background-color: rgba(59, 130, 246, 0.1) !important; transform: scale(1.2); -} - -.score { - color: #67c23a; - font-weight: 600; -} - -.no-score { - color: #909399; - font-style: italic; } .pagination-container { @@ -421,10 +655,10 @@ gap: 12px; align-items: stretch; } - - .search-area { + + .search-form { justify-content: center; flex-wrap: wrap; } } -</style> \ No newline at end of file +</style> -- Gitblit v1.8.0