From 7620d9bf9624dbbf3bf06e069e4578da3db00863 Mon Sep 17 00:00:00 2001 From: DevOps Date: Fri, 19 Dec 2025 14:43:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E8=AF=84=E5=88=86?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E9=A1=B5=E9=9D=A2=E5=92=8CAPI=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新环境配置文件 - 修改运动员和评分API - 优化登录、评分详情、评分列表等页面 - 更新pages.json和vue.config.js配置 🤖 Generated with Claude Code Co-Authored-By: Claude --- config/env.config.js | 6 +- postcss.config.js.bak | 7 + src/api/athlete.js | 14 +- src/api/score.js | 8 +- src/config/env.config.js | 5 +- src/pages.json | 4 +- src/pages/login/login.vue | 1 + src/pages/modify-score/modify-score.vue | 51 ++++- src/pages/score-detail/score-detail.vue | 64 +++++- .../score-list-multi/score-list-multi.vue | 30 ++- src/pages/score-list/score-list.vue | 197 +++++++++++++++--- vue.config.js | 11 + 12 files changed, 346 insertions(+), 52 deletions(-) create mode 100644 postcss.config.js.bak diff --git a/config/env.config.js b/config/env.config.js index fc1dd4b..7c4ed6c 100644 --- a/config/env.config.js +++ b/config/env.config.js @@ -17,7 +17,11 @@ const ENV_CONFIG = { dataMode: 'api', // API基础路径(dataMode为'api'时使用) - apiBaseURL: 'http://localhost:8123', + // uni.request 不支持 devServer proxy,必须用完整地址 + apiBaseURL: 'http://142.91.105.230:8123', + + // 调试模式 + debug: true, // 请求超时时间(毫秒) timeout: 30000, diff --git a/postcss.config.js.bak b/postcss.config.js.bak new file mode 100644 index 0000000..9d6ab7d --- /dev/null +++ b/postcss.config.js.bak @@ -0,0 +1,7 @@ +const autoprefixer = require('autoprefixer') + +module.exports = { + plugins: [ + autoprefixer() + ] +} diff --git a/src/api/athlete.js b/src/api/athlete.js index 56fb345..99af888 100644 --- a/src/api/athlete.js +++ b/src/api/athlete.js @@ -24,9 +24,12 @@ import request from '@/utils/request.js' */ export function getMyAthletes(params) { return request({ - url: '/api/mini/athletes', + url: '/mini/score/athletes', method: 'GET', - params: params, // GET 请求使用 params + params: { + ...params, + refereeType: 2 // 普通裁判 + }, showLoading: true }) } @@ -44,9 +47,12 @@ export function getMyAthletes(params) { */ export function getAthletesForAdmin(params) { return request({ - url: '/api/mini/athletes/admin', + url: '/mini/score/athletes', method: 'GET', - params: params, // GET 请求使用 params + params: { + ...params, + refereeType: 1 // 裁判长 + }, showLoading: true }) } diff --git a/src/api/score.js b/src/api/score.js index 6fbc6fe..9e9744e 100644 --- a/src/api/score.js +++ b/src/api/score.js @@ -13,7 +13,7 @@ import request from '@/utils/request.js' */ export function getDeductions(params) { return request({ - url: '/martial/deductionItem/list', + url: '/blade-martial/deductionItem/list', method: 'GET', params: { ...params, @@ -35,7 +35,7 @@ export function getDeductions(params) { */ export function submitScore(data) { return request({ - url: '/martial/score/submit', + url: '/mini/score/submit', method: 'POST', data, showLoading: true, @@ -54,7 +54,7 @@ export function submitScore(data) { */ export function getScoreDetail(params) { return request({ - url: `/api/mini/score/detail/${params.athleteId}`, + url: `/mini/score/detail/${params.athleteId}`, method: 'GET', showLoading: true }) @@ -74,7 +74,7 @@ export function getScoreDetail(params) { */ export function modifyScore(data) { return request({ - url: '/api/mini/score/modify', + url: '/mini/score/modify', method: 'PUT', data, showLoading: true, diff --git a/src/config/env.config.js b/src/config/env.config.js index fc1dd4b..bf780dd 100644 --- a/src/config/env.config.js +++ b/src/config/env.config.js @@ -17,11 +17,14 @@ const ENV_CONFIG = { dataMode: 'api', // API基础路径(dataMode为'api'时使用) - apiBaseURL: 'http://localhost:8123', + // uni.request 不支持 devServer proxy,必须用完整地址 + apiBaseURL: 'http://142.91.105.230:8123', // 请求超时时间(毫秒) timeout: 30000, + // 调试模式 + debug: true }, // 测试环境配置 diff --git a/src/pages.json b/src/pages.json index e15f2af..831b64d 100644 --- a/src/pages.json +++ b/src/pages.json @@ -11,7 +11,9 @@ "path": "pages/score-list/score-list", "style": { "navigationBarTitleText": "", - "navigationStyle": "custom" + "navigationStyle": "custom", + "enablePullDownRefresh": true, + "onReachBottomDistance": 50 } }, { diff --git a/src/pages/login/login.vue b/src/pages/login/login.vue index 2761636..c240b64 100644 --- a/src/pages/login/login.vue +++ b/src/pages/login/login.vue @@ -120,6 +120,7 @@ export default { // 保存用户信息到全局数据 getApp().globalData = { + token, // Token(用于登录状态检查) userRole, // 'pub' 或 'admin' matchCode: this.matchCode, // 比赛编码 inviteCode: this.inviteCode, // 邀请码(重要:用于后续API调用) diff --git a/src/pages/modify-score/modify-score.vue b/src/pages/modify-score/modify-score.vue index 657e462..bf313b9 100644 --- a/src/pages/modify-score/modify-score.vue +++ b/src/pages/modify-score/modify-score.vue @@ -123,6 +123,50 @@ export default { const app = getApp() 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 页面传递) const currentAthlete = globalData.currentAthlete || {} @@ -238,12 +282,11 @@ export default { // 🔥 关键改动:使用 dataAdapter 修改评分 // 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', { athleteId: this.athleteInfo.athleteId, - modifierId: this.modifierId, - modifiedScore: this.currentScore, - note: this.note + newScore: this.currentScore, + reason: this.note }) uni.hideLoading() diff --git a/src/pages/score-detail/score-detail.vue b/src/pages/score-detail/score-detail.vue index b5b77db..7dbe092 100644 --- a/src/pages/score-detail/score-detail.vue +++ b/src/pages/score-detail/score-detail.vue @@ -120,6 +120,36 @@ export default { const app = getApp() 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 页面传递) const currentAthlete = globalData.currentAthlete || {} this.player = { @@ -161,14 +191,22 @@ export default { try { // 🔥 关键改动:使用 dataAdapter 获取扣分项列表 // 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', { projectId: this.projectId }) - // 为每个扣分项添加 checked 状态 - this.deductions = (response.data || []).map(item => ({ - ...item, + // 获取返回数据(兼容分页和非分页格式) + const responseData = response.data || {} + 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 })) @@ -216,10 +254,10 @@ export default { return } - // 收集选中的扣分项ID + // 收集选中的扣分项ID(转为数字类型,后端期望 List) const selectedDeductions = this.deductions .filter(item => item.checked) - .map(item => item.deductionId) + .map(item => Number(item.deductionId)) try { uni.showLoading({ @@ -229,13 +267,19 @@ export default { // 🔥 关键改动:使用 dataAdapter 提交评分 // 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', { - athleteId: this.player.athleteId, - judgeId: this.judgeId, + athleteId: Number(this.player.athleteId), + judgeId: Number(this.judgeId), 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, - note: this.note + note: this.note || '' }) uni.hideLoading() diff --git a/src/pages/score-list-multi/score-list-multi.vue b/src/pages/score-list-multi/score-list-multi.vue index 666bee9..66ea4e2 100644 --- a/src/pages/score-list-multi/score-list-multi.vue +++ b/src/pages/score-list-multi/score-list-multi.vue @@ -116,6 +116,31 @@ export default { const app = getApp() 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 = { id: globalData.matchId, @@ -207,8 +232,11 @@ export default { // 🔥 关键改动:使用 dataAdapter 获取选手列表(裁判长视图) // 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', { + judgeId: globalData.judgeId, competitionId: this.competitionId, venueId: this.currentVenue, projectId: this.currentProject diff --git a/src/pages/score-list/score-list.vue b/src/pages/score-list/score-list.vue index 37db99d..54a17cd 100644 --- a/src/pages/score-list/score-list.vue +++ b/src/pages/score-list/score-list.vue @@ -73,6 +73,24 @@ 编号:{{ player.number }} + + + + + 加载中... + + + — 没有更多了 — + + + 上拉加载更多 + + + + + + 暂无选手数据 + @@ -101,7 +119,15 @@ export default { currentProjectIndex: 0, // 当前选中的项目索引 players: [], 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 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 = { 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: { - async loadPlayers() { - try { - uni.showLoading({ - title: '加载中...', - mask: true - }) + /** + * 加载选手列表 + * @param {Boolean} refresh - 是否刷新(重置分页) + */ + async loadPlayers(refresh = false) { + // 如果正在加载,直接返回 + if (this.isLoading) return - // 🔥 关键改动:使用 dataAdapter 获取选手列表 - // Mock模式:调用 mock/athlete.js 的 getMyAthletes 函数 - // API模式:调用 api/athlete.js 的 getMyAthletes 函数(GET /api/mini/athletes) + try { + this.isLoading = true + + // 刷新时重置分页 + if (refresh) { + this.pagination.current = 1 + this.hasMore = true + } + + // 首次加载显示loading + if (refresh) { + uni.showLoading({ + title: '加载中...', + mask: true + }) + } // 构建请求参数 - // 优先使用 matchCode(比赛编码),这样后端可以根据邀请码关联查询 const app = getApp() const globalData = app.globalData || {} const params = { - // 方案1:使用比赛编码(推荐,后端可以根据邀请码关联) + // 比赛编码 matchCode: globalData.matchCode, - - // 方案2:使用具体的ID(作为备选) + // 具体的ID judgeId: this.judgeId, 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) - uni.hideLoading() + if (refresh) { + 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 + // 判断是否还有更多数据 + const loadedCount = this.players.length + this.hasMore = loadedCount < total + // 调试信息 if (config.debug) { console.log('选手列表加载成功:', { - total: this.totalCount, - scored: this.scoredCount, - players: this.players + page: this.pagination.current, + size: this.pagination.size, + total: total, + loaded: loadedCount, + hasMore: this.hasMore, + scored: this.scoredCount }) } @@ -212,9 +309,29 @@ export default { title: error.message || '加载失败', 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) { // 保存当前选手信息到全局数据 const app = getApp() @@ -265,8 +382,8 @@ export default { }) } - // 重新加载选手列表 - await this.loadPlayers() + // 重新加载选手列表(刷新) + await this.loadPlayers(true) } } } @@ -522,4 +639,32 @@ export default { color: #666666; 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; +} diff --git a/vue.config.js b/vue.config.js index 19823ff..db46cd9 100644 --- a/vue.config.js +++ b/vue.config.js @@ -20,10 +20,21 @@ module.exports = { // 开发服务器配置 devServer: { port: 8080, + host: '0.0.0.0', open: true, overlay: { warnings: false, errors: true + }, + proxy: { + '/mini': { + target: 'http://localhost:8123', + changeOrigin: true + }, + '/martial': { + target: 'http://localhost:8123', + changeOrigin: true + } } },