Files
martial-admin-mini/pages/score-list/score-list.vue
DevOps 96bc2d92a2 Fix: 完善裁判评分列表功能
1. 修复分页问题:添加 size=200 参数确保获取所有选手
2. 裁判长页面:使用 scoringComplete 判断评分完成状态
3. 普通裁判页面:已评分选手显示分数和修改按钮
4. 修复 getAthletesForAdmin 调用正确的接口路径

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 11:18:33 +08:00

689 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<!-- 自定义导航栏 -->
<view class="nav-bar">
<view class="nav-title">评分系统</view>
<view class="nav-right">
<view class="icon-menu">···</view>
<view class="icon-close"></view>
</view>
</view>
<!-- 比赛信息 -->
<view class="match-info">
<view class="match-title">{{ matchInfo.name }}</view>
<view class="match-time">比赛时间{{ matchInfo.time }}</view>
</view>
<!-- 场地和项目选择 -->
<view class="venue-section">
<view class="venue-header">
<view
class="venue-tab"
:class="{ active: index === currentVenueIndex }"
v-for="(venue, index) in venues"
:key="venue.id"
@click="switchVenue(index)"
>
{{ venue.venueName }}
</view>
</view>
<view class="project-section">
<view
class="project-btn"
:class="{ active: index === currentProjectIndex }"
v-for="(project, index) in projects"
:key="project.id"
@click="switchProject(index)"
>
{{ project.projectName }}
</view>
</view>
</view>
<!-- 已评分统计 -->
<view class="score-stats">
<text class="stats-text">已评分</text>
<text class="stats-number">{{ scoredCount }}/{{ totalCount }}</text>
</view>
<!-- 选手列表 -->
<view class="player-list">
<!-- 遍历选手列表 -->
<view
class="player-card"
v-for="player in players"
:key="player.athleteId"
@click="handlePlayerClick(player)"
>
<view class="player-header">
<view class="player-name">{{ player.name }}</view>
<!-- 裁判长显示总分和已评分裁判数 -->
<view class="player-scores" v-if="refereeType === 1">
<text class="total-score">
总分{{ player.scoringComplete ? player.totalScore : '评分中' }}
</text>
<text class="judge-count">
已评分{{ player.scoredJudgeCount || 0 }}/{{ player.requiredJudgeCount || 0 }}
</text>
</view>
<!-- 普通裁判根据评分状态显示不同内容 -->
<view class="judge-action" v-else>
<!-- 已评分显示分数和修改按钮 -->
<view class="scored-info" v-if="player.scored">
<text class="my-score-text">我的评分{{ player.myScore }}</text>
<button
class="score-btn modify-btn"
@click.stop="goToScoreDetail(player)"
>
修改
</button>
</view>
<!-- 未评分显示评分按钮 -->
<button
class="score-btn"
v-else
@click.stop="goToScoreDetail(player)"
>
评分
</button>
</view>
</view>
<view class="player-info">
<view class="info-item" v-if="player.idCard">身份证{{ player.idCard }}</view>
<view class="info-item">队伍{{ player.team }}</view>
<view class="info-item">编号{{ player.number }}</view>
</view>
</view>
</view>
</view>
</template>
<script>
import dataAdapter from '@/utils/dataAdapter.js'
import config from '@/config/env.config.js'
export default {
data() {
return {
matchInfo: {
name: '',
time: ''
},
venueInfo: {
id: '',
name: ''
},
projectInfo: {
id: '',
name: ''
},
judgeId: '',
matchId: '',
refereeType: 2, // 裁判类型1-裁判长, 2-普通裁判)
venues: [], // 所有场地列表
currentVenueIndex: 0, // 当前选中的场地索引
projects: [], // 所有项目列表
currentProjectIndex: 0, // 当前选中的项目索引
players: [],
scoredCount: 0,
totalCount: 0
}
},
async onLoad() {
try {
// 获取全局数据
const app = getApp()
const globalData = app.globalData || {}
// 加载比赛信息
this.matchInfo = {
name: globalData.matchName || '比赛名称',
time: globalData.matchTime || '比赛时间'
}
this.judgeId = globalData.judgeId
this.matchId = globalData.matchId || globalData.matchCode
this.refereeType = globalData.refereeType || 2 // 默认为普通裁判
// 调试信息
if (config.debug) {
console.log('初始化数据:', {
judgeId: this.judgeId,
matchId: this.matchId,
matchCode: globalData.matchCode,
refereeType: this.refereeType
})
}
// 检查必要参数
if (!this.matchId) {
throw new Error('缺少比赛ID请重新登录')
}
// 显示加载提示
uni.showLoading({
title: '加载中...',
mask: true
})
// 1. 先获取场地列表
const venuesResponse = await dataAdapter.getData('getVenues', {
competitionId: this.matchId
})
this.venues = venuesResponse.data?.records || []
this.currentVenueIndex = 0
// 设置当前场地信息使用第一条数据的ID
if (this.venues.length > 0) {
this.venueInfo = {
id: this.venues[0].id,
name: this.venues[0].name
}
}
// 2. 再获取项目列表
const projectsResponse = await dataAdapter.getData('getProjects', {
competitionId: this.matchId
})
this.projects = projectsResponse.data?.records || []
this.currentProjectIndex = 0
// 设置当前项目信息使用第一条数据的ID
if (this.projects.length > 0) {
this.projectInfo = {
id: this.projects[0].id,
name: this.projects[0].projectName
}
}
uni.hideLoading()
// 调试信息
if (config.debug) {
console.log('评分列表页加载:', {
judgeId: this.judgeId,
venueId: this.venueInfo.id,
projectId: this.projectInfo.id,
venuesCount: this.venues.length,
projectsCount: this.projects.length
})
}
// 3. 最后加载选手列表使用场地和项目的第一条数据ID
await this.loadPlayers()
} catch (error) {
uni.hideLoading()
console.error('页面加载失败:', error)
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
})
}
},
methods: {
async loadPlayers() {
try {
uni.showLoading({
title: '加载中...',
mask: true
})
// 🔥 关键改动:使用 dataAdapter 获取选手列表
// Mock模式调用 mock/athlete.js 的 getMyAthletes 函数
// API模式调用 api/athlete.js 的 getMyAthletes 函数GET /api/mini/score/athletes
const response = await dataAdapter.getData('getMyAthletes', {
judgeId: this.judgeId,
refereeType: this.refereeType, // 传递裁判类型
venueId: this.venueInfo.id,
projectId: this.projectInfo.id
})
uni.hideLoading()
// 保存选手列表
this.players = response.data || []
// 计算评分统计
this.totalCount = this.players.length
this.scoredCount = this.players.filter(p => p.scored).length
// 调试信息
if (config.debug) {
console.log('选手列表加载成功:', {
total: this.totalCount,
scored: this.scoredCount,
players: this.players
})
}
} catch (error) {
uni.hideLoading()
console.error('加载选手列表失败:', error)
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
})
}
},
/**
* 处理选手卡片点击
* - 裁判长:跳转到查看详情页面
* - 普通裁判:不处理(通过评分按钮跳转)
*/
handlePlayerClick(player) {
if (this.refereeType === 1) {
// 裁判长:查看评分详情
this.goToScoreDetail(player)
}
// 普通裁判不处理卡片点击,只能通过评分按钮跳转
},
goToScoreDetail(player) {
// 保存当前选手信息、项目ID和场地ID到全局数据
const app = getApp()
app.globalData.currentAthlete = player
app.globalData.currentProjectId = this.projectInfo.id
app.globalData.currentVenueId = this.venueInfo.id
// 调试信息
if (config.debug) {
console.log('进入评分详情:', {
athleteId: player.athleteId,
athleteName: player.name,
projectId: this.projectInfo.id,
projectName: this.projectInfo.name,
venueId: this.venueInfo.id,
venueName: this.venueInfo.name,
refereeType: this.refereeType
})
}
uni.navigateTo({
url: '/pages/score-detail/score-detail'
})
},
/**
* 切换场地
* @param {Number} index - 场地索引
*/
async switchVenue(index) {
// 如果点击的是当前场地,不做处理
if (index === this.currentVenueIndex) {
return
}
// 更新当前场地索引
this.currentVenueIndex = index
// 更新当前场地信息
const currentVenue = this.venues[index] || {}
this.venueInfo = {
id: currentVenue.id,
name: currentVenue.name
}
// 调试信息
if (config.debug) {
console.log('切换场地:', {
index: index,
venueId: this.venueInfo.id,
venueName: this.venueInfo.name
})
}
// 重新加载选手列表
await this.loadPlayers()
},
/**
* 切换项目
* @param {Number} index - 项目索引
*/
async switchProject(index) {
// 如果点击的是当前项目,不做处理
if (index === this.currentProjectIndex) {
return
}
// 更新当前项目索引
this.currentProjectIndex = index
// 更新当前项目信息
const currentProject = this.projects[index] || {}
this.projectInfo = {
id: currentProject.id,
name: currentProject.projectName
}
// 调试信息
if (config.debug) {
console.log('切换项目:', {
index: index,
projectId: this.projectInfo.id,
projectName: this.projectInfo.name
})
}
// 重新加载选手列表
await this.loadPlayers()
}
}
}
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: #F5F5F5;
padding-bottom: 40rpx;
}
/* 导航栏 */
.nav-bar {
height: 90rpx;
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 0 30rpx;
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #FFFFFF;
letter-spacing: 2rpx;
}
.nav-right {
position: absolute;
right: 30rpx;
display: flex;
align-items: center;
gap: 30rpx;
}
.icon-menu,
.icon-close {
width: 60rpx;
height: 60rpx;
background-color: rgba(255, 255, 255, 0.25);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #FFFFFF;
font-weight: bold;
}
/* 比赛信息 */
.match-info {
padding: 30rpx;
background-color: #F5F5F5;
}
.match-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
line-height: 1.6;
margin-bottom: 10rpx;
}
.tip-text {
font-size: 24rpx;
color: #FF4D6A;
margin-bottom: 10rpx;
}
.match-time {
font-size: 28rpx;
color: #666666;
}
/* 场地和项目区域 */
.venue-section {
background-color: #FFFFFF;
margin: 20rpx 30rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.venue-header {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 4rpx solid #1B7C5E;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.venue-header::-webkit-scrollbar {
display: none;
}
.venue-tab {
padding: 20rpx 40rpx;
font-size: 28rpx;
font-weight: 500;
color: #666666;
background-color: #F5F5F5;
border-radius: 8rpx;
cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
white-space: nowrap;
}
.venue-tab:active {
opacity: 0.7;
}
.venue-tab.active {
font-size: 32rpx;
font-weight: 600;
color: #FFFFFF;
background-color: #1B7C5E;
}
.refresh-hint {
font-size: 24rpx;
color: #FF4D6A;
}
.project-section {
display: flex;
align-items: center;
gap: 20rpx;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.project-section::-webkit-scrollbar {
display: none;
}
.project-btn {
padding: 20rpx 40rpx;
background-color: #FFFFFF;
border: 2rpx solid #1B7C5E;
border-radius: 8rpx;
font-size: 28rpx;
color: #1B7C5E;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
white-space: nowrap;
}
.project-btn:active {
opacity: 0.7;
}
.project-btn.active {
background-color: #1B7C5E;
color: #FFFFFF;
}
.project-tip {
font-size: 22rpx;
color: #FF4D6A;
flex: 1;
margin-left: 20rpx;
line-height: 1.5;
}
/* 评分统计 */
.score-stats {
padding: 20rpx 30rpx;
font-size: 28rpx;
color: #333333;
}
.stats-text {
color: #666666;
}
.stats-number {
color: #1B7C5E;
font-weight: 600;
}
.warning-tip {
padding: 0 30rpx 20rpx;
font-size: 24rpx;
color: #FF4D6A;
}
/* 选手列表 */
.player-list {
padding: 0 30rpx;
}
.player-card {
background-color: #FFFFFF;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.player-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.player-name {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
.player-scores {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8rpx;
}
.my-score {
font-size: 26rpx;
color: #666666;
}
.total-score {
font-size: 26rpx;
color: #333333;
font-weight: 600;
}
.judge-count {
font-size: 24rpx;
color: #1B7C5E;
font-weight: 500;
}
.action-area {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8rpx;
}
.chief-hint {
font-size: 24rpx;
color: #FF4D6A;
}
.score-btn {
padding: 12rpx 40rpx;
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
border-radius: 8rpx;
font-size: 28rpx;
color: #FFFFFF;
font-weight: 500;
}
.score-btn:active {
opacity: 0.9;
}
.judge-action {
display: flex;
align-items: center;
}
.scored-info {
display: flex;
align-items: center;
gap: 20rpx;
}
.my-score-text {
font-size: 28rpx;
color: #1B7C5E;
font-weight: 600;
}
.modify-btn {
background: linear-gradient(135deg, #FF9500 0%, #FFB340 100%);
padding: 10rpx 30rpx;
font-size: 26rpx;
}
.player-info {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.info-item {
font-size: 26rpx;
color: #666666;
line-height: 1.5;
}
</style>