From b7b8947939bfcd71dec7ea3c5d8d0fddd627c860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Sun, 14 Dec 2025 17:38:35 +0800 Subject: [PATCH] fix bugs --- api/athlete.js | 78 +++--- api/auth.js | 2 +- api/score.js | 30 ++- mock/athlete.js | 93 ++++--- mock/login.js | 4 +- mock/score.js | 30 ++- package-lock.json | 32 ++- pages/login/login.vue | 4 +- pages/score-detail/score-detail.vue | 94 +++++-- pages/score-list/score-list.vue | 317 ++++++++++++++++++++---- src/api/athlete.js | 12 +- src/mock/login.js | 18 +- src/pages/login/login.vue | 3 +- src/pages/score-detail/score-detail.vue | 8 +- src/pages/score-list/score-list.vue | 108 +++++++- src/utils/request.js | 27 +- 16 files changed, 659 insertions(+), 201 deletions(-) diff --git a/api/athlete.js b/api/athlete.js index a8459de..ee1ece0 100644 --- a/api/athlete.js +++ b/api/athlete.js @@ -6,19 +6,22 @@ import request from '@/utils/request.js' /** - * 获取我的选手列表(普通评委) + * 获取选手列表(根据裁判类型返回不同数据) * @param {Object} params * @param {String} params.judgeId - 评委ID - * @param {String} params.venueId - 场地ID - * @param {String} params.projectId - 项目ID + * @param {Number} params.refereeType - 裁判类型(1-裁判长, 2-普通裁判) + * @param {String} params.venueId - 场地ID(可选) + * @param {String} params.projectId - 项目ID(可选) * @returns {Promise} * - * 注意:此接口需要后端实现 - * 建议路径: GET /api/mini/athletes + * 普通裁判:返回待评分的选手列表 + * 裁判长:返回已有评分的选手列表 + * + * 后端路径: GET /api/mini/score/athletes */ export function getMyAthletes(params) { return request({ - url: '/api/mini/athletes', + url: '/mini/score/athletes', method: 'GET', params: params, // GET 请求使用 params showLoading: true @@ -38,7 +41,7 @@ export function getMyAthletes(params) { */ export function getAthletesForAdmin(params) { return request({ - url: '/api/mini/athletes/admin', + url: '/mini/athletes/admin', method: 'GET', params: params, // GET 请求使用 params showLoading: true @@ -89,54 +92,53 @@ export default { } /** - * 后端接口规范(待实现): + * 后端接口规范: * - * GET /api/mini/athletes + * GET /api/mini/score/athletes * * 请求参数: * { * "judgeId": "456", - * "venueId": "1", - * "projectId": "5" + * "refereeType": 2, // 1-裁判长, 2-普通裁判 + * "venueId": "1", // 可选 + * "projectId": "5" // 可选 * } * - * 响应: + * 响应(普通裁判 - 待评分选手): * { * "code": 200, * "success": true, * "msg": "操作成功", * "data": [ * { - * "athleteId": "1", + * "athleteId": 1, * "name": "张三", - * "idCard": "123456789000000000", - * "team": "少林寺武术大学院", * "number": "123-4567898275", - * "myScore": 8.906, - * "totalScore": 8.907, - * "scored": true, - * "scoreTime": "2025-06-25 09:15:00" + * "team": "少林寺武术大学院", + * "projectName": "女子组长拳", + * "orderNum": 1, + * "competitionStatus": 0 * } * ] * } * - * SQL示例: - * SELECT - * a.id AS athleteId, - * a.player_name AS name, - * a.id_card AS idCard, - * a.team_name AS team, - * a.player_no AS number, - * a.total_score AS totalScore, - * s.score AS myScore, - * CASE WHEN s.id IS NOT NULL THEN 1 ELSE 0 END AS scored, - * s.score_time AS scoreTime - * FROM martial_athlete a - * LEFT JOIN martial_score s - * ON a.id = s.athlete_id - * AND s.judge_id = #{judgeId} - * WHERE a.venue_id = #{venueId} - * AND a.project_id = #{projectId} - * AND a.is_deleted = 0 - * ORDER BY a.order_num ASC + * 响应(裁判长 - 已有评分选手): + * { + * "code": 200, + * "success": true, + * "msg": "操作成功", + * "data": [ + * { + * "athleteId": 1, + * "name": "张三", + * "number": "123-4567898275", + * "team": "少林寺武术大学院", + * "projectName": "女子组长拳", + * "orderNum": 1, + * "totalScore": 8.907, + * "scoredJudgeCount": 3, + * "competitionStatus": 2 + * } + * ] + * } */ diff --git a/api/auth.js b/api/auth.js index f05a597..4a9b849 100644 --- a/api/auth.js +++ b/api/auth.js @@ -71,7 +71,7 @@ export default { * "msg": "登录成功", * "data": { * "token": "xxx", - * "userRole": "pub", + * "refereeType": 2, // 1-裁判长, 2-普通裁判 * "matchId": "123", * "matchName": "2025年全国武术散打锦标赛...", * "matchTime": "2025年6月25日 9:00", diff --git a/api/score.js b/api/score.js index 6fbc6fe..39b23c4 100644 --- a/api/score.js +++ b/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 }) @@ -82,11 +82,33 @@ export function modifyScore(data) { }) } +/** + * 获取选手列表 + * @param {Object} params + * @param {String} params.judgeId - 裁判ID + * @param {Number} params.refereeType - 裁判类型(1-裁判长, 2-普通裁判) + * @param {String} params.projectId - 项目ID(可选) + * @param {String} params.venueId - 场地ID(可选) + * @returns {Promise} + * + * 注意:此接口需要后端实现 + * 建议路径: GET /api/mini/score/athletes + */ +export function getAthletes(params) { + return request({ + url: '/api/mini/score/athletes', + method: 'GET', + params, + showLoading: true + }) +} + export default { getDeductions, submitScore, getScoreDetail, - modifyScore + modifyScore, + getAthletes } /** diff --git a/mock/athlete.js b/mock/athlete.js index 2544a52..c40e2c5 100644 --- a/mock/athlete.js +++ b/mock/athlete.js @@ -4,48 +4,77 @@ */ /** - * 获取我的选手列表(普通评委) + * 获取选手列表(根据裁判类型返回不同数据) * @param {Object} params * @param {String} params.judgeId - 评委ID - * @param {String} params.venueId - 场地ID - * @param {String} params.projectId - 项目ID - * @returns {Array} 选手列表(带评分状态) + * @param {Number} params.refereeType - 裁判类型(1-裁判长, 2-普通裁判) + * @param {String} params.venueId - 场地ID(可选) + * @param {String} params.projectId - 项目ID(可选) + * @returns {Array} 选手列表 */ export function getMyAthletes(params) { - // 模拟3个选手数据 + const { refereeType } = params + + // 裁判长:返回已有评分的选手 + if (refereeType === 1) { + return [ + { + athleteId: 1, + name: '张三', + number: '123-4567898275', + team: '少林寺武术大学院', + projectName: '女子组长拳', + orderNum: 1, + totalScore: 8.907, + scoredJudgeCount: 6, + competitionStatus: 2 + }, + { + athleteId: 2, + name: '李四', + number: '123-4567898276', + team: '武当山武术学院', + projectName: '女子组长拳', + orderNum: 2, + totalScore: 8.902, + scoredJudgeCount: 6, + competitionStatus: 2 + }, + { + athleteId: 4, + name: '赵六', + number: '123-4567898278', + team: '华山武术学院', + projectName: '女子组长拳', + orderNum: 4, + totalScore: 8.899, + scoredJudgeCount: 5, + competitionStatus: 2 + } + ] + } + + // 普通裁判:返回待评分的选手 return [ { - athleteId: '1', - name: '张三', - idCard: '123456789000000000', - team: '少林寺武术大学院', - number: '123-4567898275', - myScore: 8.906, // 我的评分 - totalScore: 8.907, // 总分 - scored: true, // 已评分 - scoreTime: '2025-06-25 09:15:00' - }, - { - athleteId: '2', - name: '李四', - idCard: '123456789000000001', - team: '武当山武术学院', - number: '123-4567898276', - myScore: 8.901, - totalScore: 8.902, - scored: true, - scoreTime: '2025-06-25 09:20:00' - }, - { - athleteId: '3', + athleteId: 3, name: '王五', idCard: '123456789000000002', team: '峨眉派武术学校', number: '123-4567898277', - myScore: null, // 未评分 - totalScore: null, - scored: false, - scoreTime: null + projectName: '女子组长拳', + orderNum: 3, + competitionStatus: 0 + }, + { + athleteId: 5, + name: '孙七', + idCard: '123456789000000004', + team: '崆峒派武术学校', + number: '123-4567898279', + projectName: '女子组长拳', + orderNum: 5, + competitionStatus: 0 } ] } diff --git a/mock/login.js b/mock/login.js index 2c6ba78..9e63254 100644 --- a/mock/login.js +++ b/mock/login.js @@ -23,8 +23,8 @@ export function login(params) { // 返回Mock登录数据 return { token: 'mock_token_' + Date.now(), - userRole: role, // 'pub' 或 'admin' - matchId: '123', + refereeType: role === 'pub' ? 2 : 1, // 1-裁判长, 2-普通裁判 + matchId: matchCode || '200', // 使用传入的比赛编码,默认200 matchName: '2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛', matchTime: '2025年6月25日 9:00', judgeId: '456', diff --git a/mock/score.js b/mock/score.js index a472f65..47f5a8a 100644 --- a/mock/score.js +++ b/mock/score.js @@ -7,20 +7,26 @@ * 获取扣分项列表 * @param {Object} params * @param {String} params.projectId - 项目ID - * @returns {Array} 扣分项列表 + * @returns {Object} 扣分项列表(包装在records中,与后端API格式一致) */ export function getDeductions(params) { - // 模拟8个扣分项 - return [ - { id: '1', text: '扣分项描述', score: -0.1, checked: false }, - { id: '2', text: '扣分项描述', score: -0.1, checked: false }, - { id: '3', text: '扣分项描述', score: -0.1, checked: false }, - { id: '4', text: '扣分项描述', score: -0.1, checked: false }, - { id: '5', text: '扣分项描述', score: -0.1, checked: false }, - { id: '6', text: '扣分项描述', score: -0.1, checked: false }, - { id: '7', text: '扣分项描述', score: -0.1, checked: false }, - { id: '8', text: '扣分项描述', score: -0.1, checked: false } - ] + // 模拟8个扣分项(字段名与后端API保持一致) + return { + records: [ + { id: '1', itemName: '动作不规范', deductionPoint: '0.1' }, + { id: '2', itemName: '节奏不稳', deductionPoint: '0.1' }, + { id: '3', itemName: '力度不足', deductionPoint: '0.1' }, + { id: '4', itemName: '平衡失误', deductionPoint: '0.1' }, + { id: '5', itemName: '器械掉落', deductionPoint: '0.2' }, + { id: '6', itemName: '出界', deductionPoint: '0.1' }, + { id: '7', itemName: '动作遗漏', deductionPoint: '0.2' }, + { id: '8', itemName: '其他失误', deductionPoint: '0.1' } + ], + total: 8, + size: 100, + current: 1, + pages: 1 + } } /** diff --git a/package-lock.json b/package-lock.json index eacefd8..6b0125f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,6 +91,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1703,6 +1704,13 @@ "node": ">=6.9.0" } }, + "node_modules/@dcloudio/types": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/@dcloudio/types/-/types-3.4.28.tgz", + "integrity": "sha512-uVIRp1VLBkrL0LaGLgIS/sT3bl1zzVTKZQbfqJEQcSAvBffRdirbSh5OvOHfA1WV5lmCAGfjhKsUQouNEVUQHg==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/@dcloudio/uni-app": { "version": "2.0.2-4080720251210002", "resolved": "https://registry.npmjs.org/@dcloudio/uni-app/-/uni-app-2.0.2-4080720251210002.tgz", @@ -3102,6 +3110,7 @@ "integrity": "sha512-+Wpvj8fMTCt9ZPOLu5YaLkFCQmB4MrZ26aRmhhKiCQ/4PMoL6mLezfqdt6c+m2htM+1WV5RunRo+0WHl2DfwZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@intervolga/optimize-cssnano-plugin": "^1.0.5", "@soda/friendly-errors-webpack-plugin": "^1.7.1", @@ -3353,6 +3362,7 @@ "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "loader-utils": "^1.2.3", "schema-utils": "^2.5.0" @@ -3430,6 +3440,7 @@ "deprecated": "3.x is no longer supported", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "html-minifier": "^3.2.3", "loader-utils": "^0.2.16", @@ -3938,6 +3949,16 @@ "node": ">=4" } }, + "node_modules/@vue/composition-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.7.2.tgz", + "integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "vue": ">= 2.5 < 2.7" + } + }, "node_modules/@vue/shared": { "version": "3.5.25", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", @@ -4204,6 +4225,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4917,7 +4939,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -5149,6 +5170,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8715,7 +8737,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, "license": "MIT", "optional": true }, @@ -8980,7 +9001,6 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -12029,7 +12049,6 @@ "version": "2.24.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "dev": true, "license": "MIT", "optional": true }, @@ -13164,6 +13183,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -18645,7 +18665,8 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==", "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/vue-hot-reload-api": { "version": "2.3.4", @@ -18894,6 +18915,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz", "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.9.0", "@webassemblyjs/helper-module-context": "1.9.0", diff --git a/pages/login/login.vue b/pages/login/login.vue index 13365cf..068024f 100644 --- a/pages/login/login.vue +++ b/pages/login/login.vue @@ -105,6 +105,7 @@ export default { const { token, userRole, + refereeType, matchId, matchName, matchTime, @@ -121,6 +122,7 @@ export default { // 保存用户信息到全局数据 getApp().globalData = { userRole, // 'pub' 或 'admin' + refereeType, // 1-裁判长, 2-普通裁判 matchCode: this.matchCode, matchId, matchName, @@ -129,7 +131,7 @@ export default { judgeName, venueId, // 普通评委有场地,裁判长为null venueName, - projects, // 分配的项目列表 + projects, // 分配的项目列表(从登录接口返回) currentProjectIndex: 0 // 当前选中的项目索引 } diff --git a/pages/score-detail/score-detail.vue b/pages/score-detail/score-detail.vue index 8de8d9f..50da31c 100644 --- a/pages/score-detail/score-detail.vue +++ b/pages/score-detail/score-detail.vue @@ -107,6 +107,8 @@ export default { }, judgeId: '', projectId: '', + competitionId: '', + venueId: '', currentScore: 8.000, note: '', minScore: 5.0, @@ -137,10 +139,9 @@ export default { // 加载评委ID和项目ID this.judgeId = globalData.judgeId - const projects = globalData.projects || [] - const currentIndex = globalData.currentProjectIndex || 0 - const currentProject = projects[currentIndex] || {} - this.projectId = currentProject.projectId + this.projectId = globalData.currentProjectId || '' + this.competitionId = globalData.matchId || globalData.matchCode || '' + this.venueId = globalData.currentVenueId || globalData.venueId || '' // 调试信息 if (config.debug) { @@ -148,6 +149,8 @@ export default { athlete: this.player, judgeId: this.judgeId, projectId: this.projectId, + competitionId: this.competitionId, + venueId: this.venueId, initialScore: this.currentScore }) } @@ -166,9 +169,12 @@ export default { projectId: this.projectId }) - // 为每个扣分项添加 checked 状态 - this.deductions = (response.data || []).map(item => ({ - ...item, + // 为每个扣分项添加 checked 状态,并映射字段名 + const records = response.data?.records || [] + this.deductions = records.map(item => ({ + deductionId: item.id, + deductionName: item.itemName, + deductionScore: parseFloat(item.deductionPoint || 0), checked: false })) @@ -187,7 +193,20 @@ export default { }, goBack() { - uni.navigateBack() + if (config.debug) { + console.log('返回上一页') + } + + uni.navigateBack({ + delta: 1, + fail: (err) => { + console.error('返回失败:', err) + // 如果返回失败,尝试跳转到评分列表页 + uni.redirectTo({ + url: '/pages/score-list/score-list' + }) + } + }) }, decreaseScore() { @@ -216,14 +235,27 @@ export default { return } - // 收集选中的扣分项 + // 验证必需字段 + if (!this.competitionId) { + uni.showToast({ + title: '缺少比赛ID,请重新登录', + icon: 'none' + }) + return + } + + if (!this.projectId) { + uni.showToast({ + title: '缺少项目ID,请返回重新选择', + icon: 'none' + }) + return + } + + // 收集选中的扣分项ID const selectedDeductions = this.deductions .filter(item => item.checked) - .map(item => ({ - deductionId: item.deductionId, - deductionName: item.deductionName, - deductionScore: item.deductionScore - })) + .map(item => item.deductionId) try { uni.showLoading({ @@ -231,16 +263,27 @@ export default { mask: true }) - // 🔥 关键改动:使用 dataAdapter 提交评分 - // Mock模式:调用 mock/score.js 的 submitScore 函数 - // API模式:调用 api/score.js 的 submitScore 函数(POST /martial/score/submit) - const response = await dataAdapter.getData('submitScore', { + // 准备提交数据 + const submitData = { athleteId: this.player.athleteId, judgeId: this.judgeId, + projectId: this.projectId, + competitionId: this.competitionId, + venueId: this.venueId, score: this.currentScore, deductions: selectedDeductions, note: this.note - }) + } + + // 调试日志:打印提交数据 + if (config.debug) { + console.log('准备提交评分数据:', submitData) + } + + // 🔥 关键改动:使用 dataAdapter 提交评分 + // Mock模式:调用 mock/score.js 的 submitScore 函数 + // API模式:调用 api/score.js 的 submitScore 函数(POST /martial/score/submit) + const response = await dataAdapter.getData('submitScore', submitData) uni.hideLoading() @@ -301,12 +344,19 @@ export default { .nav-left { position: absolute; - left: 30rpx; - width: 60rpx; - height: 60rpx; + left: 0; + top: 0; + width: 120rpx; + height: 90rpx; display: flex; align-items: center; justify-content: center; + z-index: 10; + cursor: pointer; +} + +.nav-left:active { + opacity: 0.6; } .back-icon { diff --git a/pages/score-list/score-list.vue b/pages/score-list/score-list.vue index eacd418..fa969f7 100644 --- a/pages/score-list/score-list.vue +++ b/pages/score-list/score-list.vue @@ -18,11 +18,27 @@ - {{ venueInfo.name }} + + {{ venue.venueName }} + - {{ projectInfo.name }} + + {{ project.projectName }} + @@ -39,28 +55,29 @@ class="player-card" v-for="player in players" :key="player.athleteId" + @click="handlePlayerClick(player)" > {{ player.name }} - - - 我的评分:{{ player.myScore }} - 总分:{{ player.totalScore }} + + + 总分:{{ player.totalScore || '未评分' }} + 已评分:{{ player.scoredJudgeCount || 0 }}人 - + - 身份证:{{ player.idCard }} + 身份证:{{ player.idCard }} 队伍:{{ player.team }} 编号:{{ player.number }} @@ -89,6 +106,12 @@ export default { name: '' }, judgeId: '', + matchId: '', + refereeType: 2, // 裁判类型(1-裁判长, 2-普通裁判) + venues: [], // 所有场地列表 + currentVenueIndex: 0, // 当前选中的场地索引 + projects: [], // 所有项目列表 + currentProjectIndex: 0, // 当前选中的项目索引 players: [], scoredCount: 0, totalCount: 0 @@ -96,44 +119,96 @@ export default { }, async onLoad() { - // 获取全局数据 - const app = getApp() - const globalData = app.globalData || {} + try { + // 获取全局数据 + const app = getApp() + const globalData = app.globalData || {} - // 加载比赛信息 - this.matchInfo = { - name: globalData.matchName || '比赛名称', - time: globalData.matchTime || '比赛时间' - } + // 加载比赛信息 + this.matchInfo = { + name: globalData.matchName || '比赛名称', + time: globalData.matchTime || '比赛时间' + } - // 加载场地信息 - this.venueInfo = { - id: globalData.venueId, - name: globalData.venueName || '场地' - } + this.judgeId = globalData.judgeId + this.matchId = globalData.matchId || globalData.matchCode + this.refereeType = globalData.refereeType || 2 // 默认为普通裁判 - // 加载项目信息 - const projects = globalData.projects || [] - const currentIndex = globalData.currentProjectIndex || 0 - const currentProject = projects[currentIndex] || {} - this.projectInfo = { - id: currentProject.projectId, - name: currentProject.projectName || '项目' - } + // 调试信息 + if (config.debug) { + console.log('初始化数据:', { + judgeId: this.judgeId, + matchId: this.matchId, + matchCode: globalData.matchCode, + refereeType: this.refereeType + }) + } - this.judgeId = globalData.judgeId + // 检查必要参数 + if (!this.matchId) { + throw new Error('缺少比赛ID,请重新登录') + } - // 调试信息 - if (config.debug) { - console.log('评分列表页加载:', { - judgeId: this.judgeId, - venueId: this.venueInfo.id, - projectId: this.projectInfo.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' }) } - - // 加载选手列表 - await this.loadPlayers() }, methods: { @@ -146,9 +221,10 @@ export default { // 🔥 关键改动:使用 dataAdapter 获取选手列表 // Mock模式:调用 mock/athlete.js 的 getMyAthletes 函数 - // API模式:调用 api/athlete.js 的 getMyAthletes 函数(GET /api/mini/athletes) + // 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 }) @@ -182,14 +258,108 @@ export default { } }, + /** + * 处理选手卡片点击 + * - 裁判长:跳转到查看详情页面 + * - 普通裁判:不处理(通过评分按钮跳转) + */ + 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() } } } @@ -279,26 +449,41 @@ export default { .venue-header { display: flex; align-items: center; - justify-content: space-between; + 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 { - font-size: 32rpx; - font-weight: 600; - color: #333333; - position: relative; + 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::after { - content: ''; - position: absolute; - bottom: -24rpx; - left: 0; - right: 0; - height: 4rpx; +.venue-tab:active { + opacity: 0.7; +} + +.venue-tab.active { + font-size: 32rpx; + font-weight: 600; + color: #FFFFFF; background-color: #1B7C5E; } @@ -310,7 +495,15 @@ export default { .project-section { display: flex; align-items: center; - justify-content: space-between; + gap: 20rpx; + overflow-x: auto; + overflow-y: hidden; + white-space: nowrap; + -webkit-overflow-scrolling: touch; +} + +.project-section::-webkit-scrollbar { + display: none; } .project-btn { @@ -321,6 +514,14 @@ export default { 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 { @@ -402,6 +603,12 @@ export default { font-weight: 600; } +.judge-count { + font-size: 24rpx; + color: #1B7C5E; + font-weight: 500; +} + .action-area { display: flex; flex-direction: column; diff --git a/src/api/athlete.js b/src/api/athlete.js index a8459de..56fb345 100644 --- a/src/api/athlete.js +++ b/src/api/athlete.js @@ -8,13 +8,19 @@ import request from '@/utils/request.js' /** * 获取我的选手列表(普通评委) * @param {Object} params - * @param {String} params.judgeId - 评委ID - * @param {String} params.venueId - 场地ID - * @param {String} params.projectId - 项目ID + * @param {String} params.matchCode - 比赛编码(推荐方式) + * @param {String} params.judgeId - 评委ID(备选方式) + * @param {String} params.venueId - 场地ID(备选方式) + * @param {String} params.projectId - 项目ID(备选方式) * @returns {Promise} * * 注意:此接口需要后端实现 * 建议路径: GET /api/mini/athletes + * + * 推荐实现方式: + * 1. 优先从 Token 中解析评委信息(最安全) + * 2. 或使用 matchCode 参数,后端根据 Token 中的邀请码关联查询 + * 3. 或使用 judgeId + venueId + projectId 直接查询 */ export function getMyAthletes(params) { return request({ diff --git a/src/mock/login.js b/src/mock/login.js index 2c6ba78..99d09b3 100644 --- a/src/mock/login.js +++ b/src/mock/login.js @@ -32,10 +32,22 @@ export function login(params) { // 普通评委有固定场地,裁判长可以查看所有场地 venueId: role === 'pub' ? '1' : null, venueName: role === 'pub' ? '第一场地' : null, - // 分配的项目列表 + // 分配的项目列表(对象数组格式) projects: role === 'pub' - ? ['女子组长拳', '男子组陈氏太极拳'] - : ['女子组长拳', '男子组陈氏太极拳', '女子组双剑(含长穗双剑)', '男子组杨氏太极拳', '女子组刀术', '男子组棍术', '女子组枪术', '男子组剑术'] + ? [ + { projectId: 1, projectName: '女子组长拳' }, + { projectId: 2, projectName: '男子组陈氏太极拳' } + ] + : [ + { projectId: 1, projectName: '女子组长拳' }, + { projectId: 2, projectName: '男子组陈氏太极拳' }, + { projectId: 3, projectName: '女子组双剑(含长穗双剑)' }, + { projectId: 4, projectName: '男子组杨氏太极拳' }, + { projectId: 5, projectName: '女子组刀术' }, + { projectId: 6, projectName: '男子组棍术' }, + { projectId: 7, projectName: '女子组枪术' }, + { projectId: 8, projectName: '男子组剑术' } + ] } } diff --git a/src/pages/login/login.vue b/src/pages/login/login.vue index 13365cf..2761636 100644 --- a/src/pages/login/login.vue +++ b/src/pages/login/login.vue @@ -121,7 +121,8 @@ export default { // 保存用户信息到全局数据 getApp().globalData = { userRole, // 'pub' 或 'admin' - matchCode: this.matchCode, + matchCode: this.matchCode, // 比赛编码 + inviteCode: this.inviteCode, // 邀请码(重要:用于后续API调用) matchId, matchName, matchTime, diff --git a/src/pages/score-detail/score-detail.vue b/src/pages/score-detail/score-detail.vue index 8de8d9f..b5b77db 100644 --- a/src/pages/score-detail/score-detail.vue +++ b/src/pages/score-detail/score-detail.vue @@ -216,14 +216,10 @@ export default { return } - // 收集选中的扣分项 + // 收集选中的扣分项ID const selectedDeductions = this.deductions .filter(item => item.checked) - .map(item => ({ - deductionId: item.deductionId, - deductionName: item.deductionName, - deductionScore: item.deductionScore - })) + .map(item => item.deductionId) try { uni.showLoading({ diff --git a/src/pages/score-list/score-list.vue b/src/pages/score-list/score-list.vue index eacd418..37db99d 100644 --- a/src/pages/score-list/score-list.vue +++ b/src/pages/score-list/score-list.vue @@ -22,7 +22,15 @@ - {{ projectInfo.name }} + + {{ project.projectName }} + @@ -89,6 +97,8 @@ export default { name: '' }, judgeId: '', + projects: [], // 所有分配的项目列表 + currentProjectIndex: 0, // 当前选中的项目索引 players: [], scoredCount: 0, totalCount: 0 @@ -112,14 +122,12 @@ export default { name: globalData.venueName || '场地' } - // 加载项目信息 - const projects = globalData.projects || [] - const currentIndex = globalData.currentProjectIndex || 0 - const currentProject = projects[currentIndex] || {} - this.projectInfo = { - id: currentProject.projectId, - name: currentProject.projectName || '项目' - } + // 加载项目列表 + this.projects = globalData.projects || [] + this.currentProjectIndex = globalData.currentProjectIndex || 0 + + // 设置当前项目信息 + this.updateCurrentProject() this.judgeId = globalData.judgeId @@ -128,7 +136,8 @@ export default { console.log('评分列表页加载:', { judgeId: this.judgeId, venueId: this.venueInfo.id, - projectId: this.projectInfo.id + projectId: this.projectInfo.id, + projectsCount: this.projects.length }) } @@ -147,12 +156,36 @@ export default { // 🔥 关键改动:使用 dataAdapter 获取选手列表 // Mock模式:调用 mock/athlete.js 的 getMyAthletes 函数 // API模式:调用 api/athlete.js 的 getMyAthletes 函数(GET /api/mini/athletes) - const response = await dataAdapter.getData('getMyAthletes', { + + // 构建请求参数 + // 优先使用 matchCode(比赛编码),这样后端可以根据邀请码关联查询 + const app = getApp() + const globalData = app.globalData || {} + + const params = { + // 方案1:使用比赛编码(推荐,后端可以根据邀请码关联) + matchCode: globalData.matchCode, + + // 方案2:使用具体的ID(作为备选) judgeId: this.judgeId, venueId: this.venueInfo.id, projectId: this.projectInfo.id + } + + // 移除无效参数 + Object.keys(params).forEach(key => { + if (params[key] === undefined || params[key] === null || params[key] === '') { + delete params[key] + } }) + // 调试信息 + if (config.debug) { + console.log('请求运动员列表参数:', params) + } + + const response = await dataAdapter.getData('getMyAthletes', params) + uni.hideLoading() // 保存选手列表 @@ -190,6 +223,50 @@ export default { uni.navigateTo({ url: '/pages/score-detail/score-detail' }) + }, + + /** + * 更新当前项目信息 + */ + updateCurrentProject() { + const currentProject = this.projects[this.currentProjectIndex] || {} + this.projectInfo = { + id: currentProject.projectId, + name: currentProject.projectName || '项目' + } + }, + + /** + * 切换项目 + * @param {Number} index - 项目索引 + */ + async switchProject(index) { + // 如果点击的是当前项目,不做处理 + if (index === this.currentProjectIndex) { + return + } + + // 更新当前项目索引 + this.currentProjectIndex = index + + // 更新全局数据中的项目索引 + const app = getApp() + app.globalData.currentProjectIndex = index + + // 更新当前项目信息 + this.updateCurrentProject() + + // 调试信息 + if (config.debug) { + console.log('切换项目:', { + index: index, + projectId: this.projectInfo.id, + projectName: this.projectInfo.name + }) + } + + // 重新加载选手列表 + await this.loadPlayers() } } } @@ -310,7 +387,8 @@ export default { .project-section { display: flex; align-items: center; - justify-content: space-between; + flex-wrap: wrap; + gap: 20rpx; } .project-btn { @@ -321,6 +399,12 @@ export default { font-size: 28rpx; color: #1B7C5E; font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; +} + +.project-btn:active { + opacity: 0.7; } .project-btn.active { diff --git a/src/utils/request.js b/src/utils/request.js index e55808f..578d2a9 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -70,10 +70,29 @@ function request(options = {}) { // GET请求:将params拼接到URL if (method === 'GET' && params && Object.keys(params).length > 0) { - const queryString = Object.keys(params) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) - .join('&') - fullUrl += (url.includes('?') ? '&' : '?') + queryString + // 过滤掉 undefined、null、空字符串的参数 + const validParams = Object.keys(params).filter(key => { + const value = params[key] + return value !== undefined && value !== null && value !== '' + }) + + if (validParams.length > 0) { + const queryString = validParams + .map(key => { + const value = params[key] + // 确保值不是 undefined 字符串 + if (typeof value === 'string' && value === 'undefined') { + return null + } + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}` + }) + .filter(item => item !== null) + .join('&') + + if (queryString) { + fullUrl += (url.includes('?') ? '&' : '?') + queryString + } + } requestData = undefined // GET请求不使用data字段 }