feat: 更新评分相关页面和API配置

- 更新环境配置文件
- 修改运动员和评分API
- 优化登录、评分详情、评分列表等页面
- 更新pages.json和vue.config.js配置

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DevOps
2025-12-19 14:43:16 +08:00
parent 736aa08fba
commit 7620d9bf96
12 changed files with 346 additions and 52 deletions

View File

@@ -17,7 +17,11 @@ const ENV_CONFIG = {
dataMode: 'api', dataMode: 'api',
// API基础路径dataMode为'api'时使用) // API基础路径dataMode为'api'时使用)
apiBaseURL: 'http://localhost:8123', // uni.request 不支持 devServer proxy必须用完整地址
apiBaseURL: 'http://142.91.105.230:8123',
// 调试模式
debug: true,
// 请求超时时间(毫秒) // 请求超时时间(毫秒)
timeout: 30000, timeout: 30000,

7
postcss.config.js.bak Normal file
View File

@@ -0,0 +1,7 @@
const autoprefixer = require('autoprefixer')
module.exports = {
plugins: [
autoprefixer()
]
}

View File

@@ -24,9 +24,12 @@ import request from '@/utils/request.js'
*/ */
export function getMyAthletes(params) { export function getMyAthletes(params) {
return request({ return request({
url: '/api/mini/athletes', url: '/mini/score/athletes',
method: 'GET', method: 'GET',
params: params, // GET 请求使用 params params: {
...params,
refereeType: 2 // 普通裁判
},
showLoading: true showLoading: true
}) })
} }
@@ -44,9 +47,12 @@ export function getMyAthletes(params) {
*/ */
export function getAthletesForAdmin(params) { export function getAthletesForAdmin(params) {
return request({ return request({
url: '/api/mini/athletes/admin', url: '/mini/score/athletes',
method: 'GET', method: 'GET',
params: params, // GET 请求使用 params params: {
...params,
refereeType: 1 // 裁判长
},
showLoading: true showLoading: true
}) })
} }

View File

@@ -13,7 +13,7 @@ import request from '@/utils/request.js'
*/ */
export function getDeductions(params) { export function getDeductions(params) {
return request({ return request({
url: '/martial/deductionItem/list', url: '/blade-martial/deductionItem/list',
method: 'GET', method: 'GET',
params: { params: {
...params, ...params,
@@ -35,7 +35,7 @@ export function getDeductions(params) {
*/ */
export function submitScore(data) { export function submitScore(data) {
return request({ return request({
url: '/martial/score/submit', url: '/mini/score/submit',
method: 'POST', method: 'POST',
data, data,
showLoading: true, showLoading: true,
@@ -54,7 +54,7 @@ export function submitScore(data) {
*/ */
export function getScoreDetail(params) { export function getScoreDetail(params) {
return request({ return request({
url: `/api/mini/score/detail/${params.athleteId}`, url: `/mini/score/detail/${params.athleteId}`,
method: 'GET', method: 'GET',
showLoading: true showLoading: true
}) })
@@ -74,7 +74,7 @@ export function getScoreDetail(params) {
*/ */
export function modifyScore(data) { export function modifyScore(data) {
return request({ return request({
url: '/api/mini/score/modify', url: '/mini/score/modify',
method: 'PUT', method: 'PUT',
data, data,
showLoading: true, showLoading: true,

View File

@@ -17,11 +17,14 @@ const ENV_CONFIG = {
dataMode: 'api', dataMode: 'api',
// API基础路径dataMode为'api'时使用) // API基础路径dataMode为'api'时使用)
apiBaseURL: 'http://localhost:8123', // uni.request 不支持 devServer proxy必须用完整地址
apiBaseURL: 'http://142.91.105.230:8123',
// 请求超时时间(毫秒) // 请求超时时间(毫秒)
timeout: 30000, timeout: 30000,
// 调试模式
debug: true
}, },
// 测试环境配置 // 测试环境配置

View File

@@ -11,7 +11,9 @@
"path": "pages/score-list/score-list", "path": "pages/score-list/score-list",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
"navigationStyle": "custom" "navigationStyle": "custom",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50
} }
}, },
{ {

View File

@@ -120,6 +120,7 @@ export default {
// 保存用户信息到全局数据 // 保存用户信息到全局数据
getApp().globalData = { getApp().globalData = {
token, // Token用于登录状态检查
userRole, // 'pub' 或 'admin' userRole, // 'pub' 或 'admin'
matchCode: this.matchCode, // 比赛编码 matchCode: this.matchCode, // 比赛编码
inviteCode: this.inviteCode, // 邀请码重要用于后续API调用 inviteCode: this.inviteCode, // 邀请码重要用于后续API调用

View File

@@ -123,6 +123,50 @@ export default {
const app = getApp() const app = getApp()
const globalData = app.globalData || {} const globalData = app.globalData || {}
// 检查登录状态
if (!globalData.judgeId || !globalData.token) {
console.warn('用户未登录,跳转到登录页')
uni.showToast({
title: '请先登录',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
return
}
// 检查是否是裁判长
if (globalData.userRole !== 'admin') {
console.warn('非裁判长用户,无权修改评分')
uni.showToast({
title: '无权限',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
return
}
// 检查是否有选手信息
if (!globalData.currentAthlete || !globalData.currentAthlete.athleteId) {
console.warn('没有选手信息,返回列表页')
uni.showToast({
title: '请选择选手',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
return
}
// 获取当前选手信息(从 score-list-multi 页面传递) // 获取当前选手信息(从 score-list-multi 页面传递)
const currentAthlete = globalData.currentAthlete || {} const currentAthlete = globalData.currentAthlete || {}
@@ -238,12 +282,11 @@ export default {
// 🔥 关键改动:使用 dataAdapter 修改评分 // 🔥 关键改动:使用 dataAdapter 修改评分
// Mock模式调用 mock/score.js 的 modifyScore 函数 // Mock模式调用 mock/score.js 的 modifyScore 函数
// API模式调用 api/score.js 的 modifyScore 函数PUT /api/mini/score/modify // API模式调用 api/score.js 的 modifyScore 函数PUT /mini/score/modify
const response = await dataAdapter.getData('modifyScore', { const response = await dataAdapter.getData('modifyScore', {
athleteId: this.athleteInfo.athleteId, athleteId: this.athleteInfo.athleteId,
modifierId: this.modifierId, newScore: this.currentScore,
modifiedScore: this.currentScore, reason: this.note
note: this.note
}) })
uni.hideLoading() uni.hideLoading()

View File

@@ -120,6 +120,36 @@ export default {
const app = getApp() const app = getApp()
const globalData = app.globalData || {} const globalData = app.globalData || {}
// 检查登录状态
if (!globalData.judgeId || !globalData.token) {
console.warn('用户未登录,跳转到登录页')
uni.showToast({
title: '请先登录',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
return
}
// 检查是否有选手信息
if (!globalData.currentAthlete || !globalData.currentAthlete.athleteId) {
console.warn('没有选手信息,返回列表页')
uni.showToast({
title: '请选择选手',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
return
}
// 加载当前选手信息(从 score-list 页面传递) // 加载当前选手信息(从 score-list 页面传递)
const currentAthlete = globalData.currentAthlete || {} const currentAthlete = globalData.currentAthlete || {}
this.player = { this.player = {
@@ -161,14 +191,22 @@ export default {
try { try {
// 🔥 关键改动:使用 dataAdapter 获取扣分项列表 // 🔥 关键改动:使用 dataAdapter 获取扣分项列表
// Mock模式调用 mock/score.js 的 getDeductions 函数 // Mock模式调用 mock/score.js 的 getDeductions 函数
// API模式调用 api/score.js 的 getDeductions 函数GET /martial/deductionItem/list // API模式调用 api/score.js 的 getDeductions 函数GET /blade-martial/deductionItem/list
const response = await dataAdapter.getData('getDeductions', { const response = await dataAdapter.getData('getDeductions', {
projectId: this.projectId projectId: this.projectId
}) })
// 为每个扣分项添加 checked 状态 // 获取返回数据(兼容分页和非分页格式)
this.deductions = (response.data || []).map(item => ({ const responseData = response.data || {}
...item, const records = responseData.records || response.data || []
// 为每个扣分项添加 checked 状态,并映射字段名
// 后端字段: id, itemName
// 前端字段: deductionId, deductionName
this.deductions = (Array.isArray(records) ? records : []).map(item => ({
deductionId: item.deductionId || item.id,
deductionName: item.deductionName || item.itemName,
deductionPoint: item.deductionPoint || 0,
checked: false checked: false
})) }))
@@ -216,10 +254,10 @@ export default {
return return
} }
// 收集选中的扣分项ID // 收集选中的扣分项ID(转为数字类型,后端期望 List<Long>
const selectedDeductions = this.deductions const selectedDeductions = this.deductions
.filter(item => item.checked) .filter(item => item.checked)
.map(item => item.deductionId) .map(item => Number(item.deductionId))
try { try {
uni.showLoading({ uni.showLoading({
@@ -229,13 +267,19 @@ export default {
// 🔥 关键改动:使用 dataAdapter 提交评分 // 🔥 关键改动:使用 dataAdapter 提交评分
// Mock模式调用 mock/score.js 的 submitScore 函数 // Mock模式调用 mock/score.js 的 submitScore 函数
// API模式调用 api/score.js 的 submitScore 函数POST /martial/score/submit // API模式调用 api/score.js 的 submitScore 函数POST /mini/score/submit
const app = getApp()
const globalData = app.globalData || {}
const response = await dataAdapter.getData('submitScore', { const response = await dataAdapter.getData('submitScore', {
athleteId: this.player.athleteId, athleteId: Number(this.player.athleteId),
judgeId: this.judgeId, judgeId: Number(this.judgeId),
score: this.currentScore, score: this.currentScore,
projectId: Number(this.projectId),
competitionId: globalData.matchId ? Number(globalData.matchId) : null,
venueId: globalData.venueId ? Number(globalData.venueId) : null,
scheduleId: globalData.scheduleId ? Number(globalData.scheduleId) : null,
deductions: selectedDeductions, deductions: selectedDeductions,
note: this.note note: this.note || ''
}) })
uni.hideLoading() uni.hideLoading()

View File

@@ -116,6 +116,31 @@ export default {
const app = getApp() const app = getApp()
const globalData = app.globalData || {} const globalData = app.globalData || {}
// 检查登录状态
if (!globalData.judgeId || !globalData.token) {
console.warn('用户未登录,跳转到登录页')
uni.showToast({
title: '请先登录',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
return
}
// 检查是否是裁判长
if (globalData.userRole !== 'admin') {
console.warn('非裁判长用户,跳转到普通评分页')
uni.reLaunch({
url: '/pages/score-list/score-list'
})
return
}
// 加载比赛信息 // 加载比赛信息
this.matchInfo = { this.matchInfo = {
id: globalData.matchId, id: globalData.matchId,
@@ -207,8 +232,11 @@ export default {
// 🔥 关键改动:使用 dataAdapter 获取选手列表(裁判长视图) // 🔥 关键改动:使用 dataAdapter 获取选手列表(裁判长视图)
// Mock模式调用 mock/athlete.js 的 getAthletesForAdmin 函数 // Mock模式调用 mock/athlete.js 的 getAthletesForAdmin 函数
// API模式调用 api/athlete.js 的 getAthletesForAdmin 函数GET /api/mini/athletes/admin // API模式调用 api/athlete.js 的 getAthletesForAdmin 函数GET /mini/score/athletes
const app = getApp()
const globalData = app.globalData || {}
const response = await dataAdapter.getData('getAthletesForAdmin', { const response = await dataAdapter.getData('getAthletesForAdmin', {
judgeId: globalData.judgeId,
competitionId: this.competitionId, competitionId: this.competitionId,
venueId: this.currentVenue, venueId: this.currentVenue,
projectId: this.currentProject projectId: this.currentProject

View File

@@ -73,6 +73,24 @@
<view class="info-item">编号{{ player.number }}</view> <view class="info-item">编号{{ player.number }}</view>
</view> </view>
</view> </view>
<!-- 加载状态 -->
<view class="loading-status" v-if="players.length > 0">
<view v-if="isLoading" class="loading-more">
<text>加载中...</text>
</view>
<view v-else-if="!hasMore" class="no-more">
<text> 没有更多了 </text>
</view>
<view v-else class="load-more-tip">
<text>上拉加载更多</text>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="!isLoading && players.length === 0">
<text class="empty-text">暂无选手数据</text>
</view>
</view> </view>
</view> </view>
</template> </template>
@@ -101,7 +119,15 @@ export default {
currentProjectIndex: 0, // 当前选中的项目索引 currentProjectIndex: 0, // 当前选中的项目索引
players: [], players: [],
scoredCount: 0, scoredCount: 0,
totalCount: 0 totalCount: 0,
// 分页相关
pagination: {
current: 1, // 当前页码
size: 10, // 每页条数
total: 0 // 总条数
},
isLoading: false, // 是否正在加载
hasMore: true // 是否还有更多数据
} }
}, },
@@ -110,6 +136,22 @@ export default {
const app = getApp() const app = getApp()
const globalData = app.globalData || {} const globalData = app.globalData || {}
// 检查登录状态
if (!globalData.judgeId || !globalData.token) {
console.warn('用户未登录,跳转到登录页')
uni.showToast({
title: '请先登录',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
return
}
// 加载比赛信息 // 加载比赛信息
this.matchInfo = { this.matchInfo = {
name: globalData.matchName || '比赛名称', name: globalData.matchName || '比赛名称',
@@ -141,35 +183,69 @@ export default {
}) })
} }
// 加载选手列表 // 加载选手列表(第一页)
await this.loadPlayers() await this.loadPlayers(true)
},
// 下拉刷新
async onPullDownRefresh() {
if (config.debug) {
console.log('下拉刷新')
}
await this.loadPlayers(true)
uni.stopPullDownRefresh()
},
// 上拉加载更多
async onReachBottom() {
if (config.debug) {
console.log('上拉加载更多, hasMore:', this.hasMore, 'isLoading:', this.isLoading)
}
if (this.hasMore && !this.isLoading) {
await this.loadMore()
}
}, },
methods: { methods: {
async loadPlayers() { /**
* 加载选手列表
* @param {Boolean} refresh - 是否刷新(重置分页)
*/
async loadPlayers(refresh = false) {
// 如果正在加载,直接返回
if (this.isLoading) return
try { try {
this.isLoading = true
// 刷新时重置分页
if (refresh) {
this.pagination.current = 1
this.hasMore = true
}
// 首次加载显示loading
if (refresh) {
uni.showLoading({ uni.showLoading({
title: '加载中...', title: '加载中...',
mask: true mask: true
}) })
}
// 🔥 关键改动:使用 dataAdapter 获取选手列表
// Mock模式调用 mock/athlete.js 的 getMyAthletes 函数
// API模式调用 api/athlete.js 的 getMyAthletes 函数GET /api/mini/athletes
// 构建请求参数 // 构建请求参数
// 优先使用 matchCode比赛编码这样后端可以根据邀请码关联查询
const app = getApp() const app = getApp()
const globalData = app.globalData || {} const globalData = app.globalData || {}
const params = { const params = {
// 方案1使用比赛编码推荐后端可以根据邀请码关联 // 比赛编码
matchCode: globalData.matchCode, matchCode: globalData.matchCode,
// 具体的ID
// 方案2使用具体的ID作为备选
judgeId: this.judgeId, judgeId: this.judgeId,
venueId: this.venueInfo.id, venueId: this.venueInfo.id,
projectId: this.projectInfo.id projectId: this.projectInfo.id,
// 分页参数
current: this.pagination.current,
size: this.pagination.size
} }
// 移除无效参数 // 移除无效参数
@@ -186,21 +262,42 @@ export default {
const response = await dataAdapter.getData('getMyAthletes', params) const response = await dataAdapter.getData('getMyAthletes', params)
if (refresh) {
uni.hideLoading() uni.hideLoading()
}
// 保存选手列表 // 获取返回数据
this.players = response.data || [] const responseData = response.data || {}
const records = responseData.records || response.data || []
const total = responseData.total || records.length
// 更新分页信息
this.pagination.total = total
this.totalCount = total
// 刷新时替换数据,否则追加数据
if (refresh) {
this.players = records
} else {
this.players = [...this.players, ...records]
}
// 计算评分统计 // 计算评分统计
this.totalCount = this.players.length
this.scoredCount = this.players.filter(p => p.scored).length this.scoredCount = this.players.filter(p => p.scored).length
// 判断是否还有更多数据
const loadedCount = this.players.length
this.hasMore = loadedCount < total
// 调试信息 // 调试信息
if (config.debug) { if (config.debug) {
console.log('选手列表加载成功:', { console.log('选手列表加载成功:', {
total: this.totalCount, page: this.pagination.current,
scored: this.scoredCount, size: this.pagination.size,
players: this.players total: total,
loaded: loadedCount,
hasMore: this.hasMore,
scored: this.scoredCount
}) })
} }
@@ -212,9 +309,29 @@ export default {
title: error.message || '加载失败', title: error.message || '加载失败',
icon: 'none' icon: 'none'
}) })
} finally {
this.isLoading = false
} }
}, },
/**
* 加载更多
*/
async loadMore() {
if (!this.hasMore || this.isLoading) return
// 页码+1
this.pagination.current++
// 调试信息
if (config.debug) {
console.log('加载更多, 页码:', this.pagination.current)
}
// 加载下一页
await this.loadPlayers(false)
},
goToScoreDetail(player) { goToScoreDetail(player) {
// 保存当前选手信息到全局数据 // 保存当前选手信息到全局数据
const app = getApp() const app = getApp()
@@ -265,8 +382,8 @@ export default {
}) })
} }
// 重新加载选手列表 // 重新加载选手列表(刷新)
await this.loadPlayers() await this.loadPlayers(true)
} }
} }
} }
@@ -522,4 +639,32 @@ export default {
color: #666666; color: #666666;
line-height: 1.5; line-height: 1.5;
} }
/* 加载状态 */
.loading-status {
padding: 30rpx 0;
text-align: center;
}
.loading-more,
.no-more,
.load-more-tip {
font-size: 26rpx;
color: #999999;
}
.loading-more {
color: #1B7C5E;
}
/* 空状态 */
.empty-state {
padding: 100rpx 0;
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
</style> </style>

View File

@@ -20,10 +20,21 @@ module.exports = {
// 开发服务器配置 // 开发服务器配置
devServer: { devServer: {
port: 8080, port: 8080,
host: '0.0.0.0',
open: true, open: true,
overlay: { overlay: {
warnings: false, warnings: false,
errors: true errors: true
},
proxy: {
'/mini': {
target: 'http://localhost:8123',
changeOrigin: true
},
'/martial': {
target: 'http://localhost:8123',
changeOrigin: true
}
} }
}, },