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>
689 lines
15 KiB
Vue
689 lines
15 KiB
Vue
<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>
|