fix bugs
This commit is contained in:
@@ -9,7 +9,8 @@
|
|||||||
"Bash(git checkout:*)",
|
"Bash(git checkout:*)",
|
||||||
"Bash(ls:*)",
|
"Bash(ls:*)",
|
||||||
"Bash(done)",
|
"Bash(done)",
|
||||||
"Bash(cat:*)"
|
"Bash(cat:*)",
|
||||||
|
"Bash(git push:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
20142
package-lock.json
generated
20142
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@@ -3,5 +3,50 @@
|
|||||||
"name": "martial-admin-mini",
|
"name": "martial-admin-mini",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "武术比赛评分系统",
|
"description": "武术比赛评分系统",
|
||||||
"main": "main.js"
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service serve",
|
||||||
|
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/uni-cli-i18n": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/uni-h5": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/uni-i18n": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/uni-migration": "^2.0.2-3081220230817001",
|
||||||
|
"autoprefixer": "^9.8.8",
|
||||||
|
"cache-loader": "^4.1.0",
|
||||||
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
|
"css-loader": "^3.6.0",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"html-webpack-plugin": "^4.5.2",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"sass": "^1.96.0",
|
||||||
|
"sass-loader": "^10.5.2",
|
||||||
|
"thread-loader": "^2.1.3",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"vue": "^2.6.14",
|
||||||
|
"vue-loader": "^15.11.1",
|
||||||
|
"webpack": "^4.47.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@dcloudio/uni-cli-shared": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/uni-template-compiler": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/vue-cli-plugin-uni": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/webpack-uni-mp-loader": "^2.0.2-3081220230817001",
|
||||||
|
"@dcloudio/webpack-uni-pages-loader": "^2.0.2-3081220230817001",
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.19",
|
||||||
|
"@vue/cli-service": "~4.5.19",
|
||||||
|
"babel-plugin-import": "^1.13.5",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"mini-types": "*",
|
||||||
|
"postcss-comment": "^2.0.0",
|
||||||
|
"vue-template-compiler": "^2.6.14"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"Android >= 4.4",
|
||||||
|
"ios >= 9"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
7
postcss.config.js
Normal file
7
postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const autoprefixer = require('autoprefixer')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
autoprefixer()
|
||||||
|
]
|
||||||
|
}
|
||||||
18
src/App.vue
Normal file
18
src/App.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
onLaunch: function() {
|
||||||
|
console.log('App Launch')
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
console.log('App Show')
|
||||||
|
},
|
||||||
|
onHide: function() {
|
||||||
|
console.log('App Hide')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
|
||||||
|
@import "common/common.css";
|
||||||
|
</style>
|
||||||
142
src/api/athlete.js
Normal file
142
src/api/athlete.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* API接口 - 选手模块
|
||||||
|
* 真实后端接口调用(需要后端实现)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import request from '@/utils/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的选手列表(普通评委)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.judgeId - 评委ID
|
||||||
|
* @param {String} params.venueId - 场地ID
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*
|
||||||
|
* 注意:此接口需要后端实现
|
||||||
|
* 建议路径: GET /api/mini/athletes
|
||||||
|
*/
|
||||||
|
export function getMyAthletes(params) {
|
||||||
|
return request({
|
||||||
|
url: '/api/mini/athletes',
|
||||||
|
method: 'GET',
|
||||||
|
params: params, // GET 请求使用 params
|
||||||
|
showLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选手列表(裁判长)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @param {String} params.venueId - 场地ID
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*
|
||||||
|
* 注意:此接口需要后端实现
|
||||||
|
* 建议路径: GET /api/mini/athletes/admin
|
||||||
|
*/
|
||||||
|
export function getAthletesForAdmin(params) {
|
||||||
|
return request({
|
||||||
|
url: '/api/mini/athletes/admin',
|
||||||
|
method: 'GET',
|
||||||
|
params: params, // GET 请求使用 params
|
||||||
|
showLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function getVenues(params) {
|
||||||
|
return request({
|
||||||
|
url: '/martial/venue/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current: 1,
|
||||||
|
size: 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function getProjects(params) {
|
||||||
|
return request({
|
||||||
|
url: '/martial/project/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current: 1,
|
||||||
|
size: 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getMyAthletes,
|
||||||
|
getAthletesForAdmin,
|
||||||
|
getVenues,
|
||||||
|
getProjects
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端接口规范(待实现):
|
||||||
|
*
|
||||||
|
* GET /api/mini/athletes
|
||||||
|
*
|
||||||
|
* 请求参数:
|
||||||
|
* {
|
||||||
|
* "judgeId": "456",
|
||||||
|
* "venueId": "1",
|
||||||
|
* "projectId": "5"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 响应:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "success": true,
|
||||||
|
* "msg": "操作成功",
|
||||||
|
* "data": [
|
||||||
|
* {
|
||||||
|
* "athleteId": "1",
|
||||||
|
* "name": "张三",
|
||||||
|
* "idCard": "123456789000000000",
|
||||||
|
* "team": "少林寺武术大学院",
|
||||||
|
* "number": "123-4567898275",
|
||||||
|
* "myScore": 8.906,
|
||||||
|
* "totalScore": 8.907,
|
||||||
|
* "scored": true,
|
||||||
|
* "scoreTime": "2025-06-25 09:15:00"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
85
src/api/auth.js
Normal file
85
src/api/auth.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* API接口 - 认证模块
|
||||||
|
* 真实后端接口调用(需要后端实现)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import request from '@/utils/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录验证
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {String} data.matchCode - 比赛编码
|
||||||
|
* @param {String} data.inviteCode - 邀请码
|
||||||
|
* @returns {Promise}
|
||||||
|
*
|
||||||
|
* 注意:此接口需要后端实现
|
||||||
|
* 建议路径: POST /api/mini/login
|
||||||
|
*/
|
||||||
|
export function login(data) {
|
||||||
|
return request({
|
||||||
|
url: '/mini/login',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
showLoading: true,
|
||||||
|
loadingText: '登录中...'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function logout() {
|
||||||
|
return request({
|
||||||
|
url: '/mini/logout',
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token验证
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function verifyToken() {
|
||||||
|
return request({
|
||||||
|
url: '/mini/verify',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
verifyToken
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端接口规范(待实现):
|
||||||
|
*
|
||||||
|
* POST /api/mini/login
|
||||||
|
*
|
||||||
|
* 请求:
|
||||||
|
* {
|
||||||
|
* "matchCode": "123",
|
||||||
|
* "inviteCode": "pub"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 响应:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "success": true,
|
||||||
|
* "msg": "登录成功",
|
||||||
|
* "data": {
|
||||||
|
* "token": "xxx",
|
||||||
|
* "userRole": "pub",
|
||||||
|
* "matchId": "123",
|
||||||
|
* "matchName": "2025年全国武术散打锦标赛...",
|
||||||
|
* "matchTime": "2025年6月25日 9:00",
|
||||||
|
* "judgeId": "456",
|
||||||
|
* "judgeName": "欧阳丽娜",
|
||||||
|
* "venueId": "1",
|
||||||
|
* "venueName": "第一场地",
|
||||||
|
* "projects": ["女子组长拳", "男子组陈氏太极拳"]
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
158
src/api/index.js
Normal file
158
src/api/index.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* API接口中心
|
||||||
|
* 所有API接口的统一入口
|
||||||
|
*
|
||||||
|
* 这个文件汇总了所有业务模块的API接口函数,
|
||||||
|
* 提供给 dataAdapter.js 调用
|
||||||
|
*/
|
||||||
|
|
||||||
|
import authApi from './auth.js'
|
||||||
|
import athleteApi from './athlete.js'
|
||||||
|
import scoreApi from './score.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出所有API接口函数
|
||||||
|
*
|
||||||
|
* 资源名称(key)对应 dataAdapter.getData() 的第一个参数
|
||||||
|
* 例如:dataAdapter.getData('login', params) 会调用 authApi.login(params)
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
// ==================== 认证模块 ====================
|
||||||
|
/**
|
||||||
|
* 登录验证
|
||||||
|
* @param {Object} data - { matchCode, inviteCode }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
login: authApi.login,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
logout: authApi.logout,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token验证
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
verifyToken: authApi.verifyToken,
|
||||||
|
|
||||||
|
// ==================== 选手模块 ====================
|
||||||
|
/**
|
||||||
|
* 获取我的选手列表(普通评委)
|
||||||
|
* @param {Object} params - { judgeId, venueId, projectId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getMyAthletes: athleteApi.getMyAthletes,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选手列表(裁判长)
|
||||||
|
* @param {Object} params - { competitionId, venueId, projectId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getAthletesForAdmin: athleteApi.getAthletesForAdmin,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地列表
|
||||||
|
* @param {Object} params - { competitionId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getVenues: athleteApi.getVenues,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目列表
|
||||||
|
* @param {Object} params - { competitionId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getProjects: athleteApi.getProjects,
|
||||||
|
|
||||||
|
// ==================== 评分模块 ====================
|
||||||
|
/**
|
||||||
|
* 获取扣分项列表
|
||||||
|
* @param {Object} params - { projectId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getDeductions: scoreApi.getDeductions,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交评分
|
||||||
|
* @param {Object} data - { athleteId, judgeId, score, deductions, note }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
submitScore: scoreApi.submitScore,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评分详情(裁判长查看)
|
||||||
|
* @param {Object} params - { athleteId }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getScoreDetail: scoreApi.getScoreDetail,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改评分(裁判长)
|
||||||
|
* @param {Object} data - { athleteId, modifierId, modifiedScore, note }
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
modifyScore: scoreApi.modifyScore
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用说明:
|
||||||
|
*
|
||||||
|
* 这个文件不直接在页面中使用,而是通过 dataAdapter.js 间接调用。
|
||||||
|
*
|
||||||
|
* 当 config/env.config.js 中 dataMode 设置为 'api' 时,
|
||||||
|
* dataAdapter.getData() 会自动调用这里的API函数。
|
||||||
|
*
|
||||||
|
* 页面使用示例:
|
||||||
|
*
|
||||||
|
* import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
*
|
||||||
|
* // 配置 dataMode: 'api' 时,以下代码会调用真实API
|
||||||
|
* const res = await dataAdapter.getData('login', {
|
||||||
|
* matchCode: '123',
|
||||||
|
* inviteCode: 'pub'
|
||||||
|
* })
|
||||||
|
* // 实际调用: authApi.login({ matchCode, inviteCode })
|
||||||
|
* // 请求: POST /api/mini/login
|
||||||
|
*
|
||||||
|
* // 配置 dataMode: 'mock' 时,同样的代码会使用Mock数据
|
||||||
|
* // 实际调用: mockData.login({ matchCode, inviteCode })
|
||||||
|
* // 无网络请求,返回本地Mock数据
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端开发者注意事项:
|
||||||
|
*
|
||||||
|
* 1. 需要实现的新接口(小程序专用):
|
||||||
|
* - POST /api/mini/login # 登录验证
|
||||||
|
* - GET /api/mini/athletes # 普通评委选手列表
|
||||||
|
* - GET /api/mini/athletes/admin # 裁判长选手列表
|
||||||
|
* - GET /api/mini/score/detail/{athleteId} # 评分详情
|
||||||
|
* - PUT /api/mini/score/modify # 修改评分
|
||||||
|
*
|
||||||
|
* 2. 可以复用的现有接口:
|
||||||
|
* - POST /martial/score/submit # 提交评分
|
||||||
|
* - GET /martial/venue/list # 场地列表
|
||||||
|
* - GET /martial/project/list # 项目列表
|
||||||
|
* - GET /martial/deductionItem/list # 扣分项列表
|
||||||
|
*
|
||||||
|
* 3. 响应格式统一为 BladeX 标准格式:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "success": true,
|
||||||
|
* "msg": "操作成功",
|
||||||
|
* "data": { ... }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 4. 请求头要求:
|
||||||
|
* - Content-Type: application/json
|
||||||
|
* - Blade-Auth: Bearer {token}
|
||||||
|
*
|
||||||
|
* 5. 建议创建专门的Controller:
|
||||||
|
* @RestController
|
||||||
|
* @RequestMapping("/api/mini")
|
||||||
|
* public class MartialMiniController {
|
||||||
|
* // 实现上述5个专用接口
|
||||||
|
* }
|
||||||
|
*/
|
||||||
165
src/api/score.js
Normal file
165
src/api/score.js
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* API接口 - 评分模块
|
||||||
|
* 真实后端接口调用(需要后端实现)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import request from '@/utils/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扣分项列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function getDeductions(params) {
|
||||||
|
return request({
|
||||||
|
url: '/martial/deductionItem/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current: 1,
|
||||||
|
size: 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交评分
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {String} data.athleteId - 选手ID
|
||||||
|
* @param {String} data.judgeId - 评委ID
|
||||||
|
* @param {Number} data.score - 评分
|
||||||
|
* @param {Array} data.deductions - 扣分项
|
||||||
|
* @param {String} data.note - 备注
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function submitScore(data) {
|
||||||
|
return request({
|
||||||
|
url: '/martial/score/submit',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
showLoading: true,
|
||||||
|
loadingText: '提交中...'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评分详情(裁判长查看)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.athleteId - 选手ID
|
||||||
|
* @returns {Promise}
|
||||||
|
*
|
||||||
|
* 注意:此接口需要后端实现
|
||||||
|
* 建议路径: GET /api/mini/score/detail/{athleteId}
|
||||||
|
*/
|
||||||
|
export function getScoreDetail(params) {
|
||||||
|
return request({
|
||||||
|
url: `/api/mini/score/detail/${params.athleteId}`,
|
||||||
|
method: 'GET',
|
||||||
|
showLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改评分(裁判长)
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {String} data.athleteId - 选手ID
|
||||||
|
* @param {String} data.modifierId - 修改人ID
|
||||||
|
* @param {Number} data.modifiedScore - 修改后的分数
|
||||||
|
* @param {String} data.note - 修改原因
|
||||||
|
* @returns {Promise}
|
||||||
|
*
|
||||||
|
* 注意:此接口需要后端实现
|
||||||
|
* 建议路径: PUT /api/mini/score/modify
|
||||||
|
*/
|
||||||
|
export function modifyScore(data) {
|
||||||
|
return request({
|
||||||
|
url: '/api/mini/score/modify',
|
||||||
|
method: 'PUT',
|
||||||
|
data,
|
||||||
|
showLoading: true,
|
||||||
|
loadingText: '修改中...'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getDeductions,
|
||||||
|
submitScore,
|
||||||
|
getScoreDetail,
|
||||||
|
modifyScore
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端接口规范(待实现):
|
||||||
|
*
|
||||||
|
* 1. GET /api/mini/score/detail/{athleteId}
|
||||||
|
*
|
||||||
|
* 响应:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "success": true,
|
||||||
|
* "msg": "操作成功",
|
||||||
|
* "data": {
|
||||||
|
* "athleteInfo": {
|
||||||
|
* "athleteId": "1",
|
||||||
|
* "name": "张三",
|
||||||
|
* "idCard": "123456789000000000",
|
||||||
|
* "team": "少林寺武术大学院",
|
||||||
|
* "number": "123-4567898275",
|
||||||
|
* "totalScore": 8.907
|
||||||
|
* },
|
||||||
|
* "judgeScores": [
|
||||||
|
* {
|
||||||
|
* "judgeId": "1",
|
||||||
|
* "judgeName": "欧阳丽娜",
|
||||||
|
* "score": 8.907,
|
||||||
|
* "scoreTime": "2025-06-25 09:15:00",
|
||||||
|
* "note": ""
|
||||||
|
* }
|
||||||
|
* ],
|
||||||
|
* "modification": null
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* SQL示例:
|
||||||
|
* SELECT
|
||||||
|
* s.judge_id AS judgeId,
|
||||||
|
* s.judge_name AS judgeName,
|
||||||
|
* s.score,
|
||||||
|
* s.score_time AS scoreTime,
|
||||||
|
* s.note
|
||||||
|
* FROM martial_score s
|
||||||
|
* WHERE s.athlete_id = #{athleteId}
|
||||||
|
* ORDER BY s.score_time ASC
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* 2. PUT /api/mini/score/modify
|
||||||
|
*
|
||||||
|
* 请求:
|
||||||
|
* {
|
||||||
|
* "athleteId": "1",
|
||||||
|
* "modifierId": "789",
|
||||||
|
* "modifiedScore": 8.910,
|
||||||
|
* "note": "修改原因"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 响应:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "success": true,
|
||||||
|
* "msg": "修改成功",
|
||||||
|
* "data": {
|
||||||
|
* "athleteId": "1",
|
||||||
|
* "originalScore": 8.907,
|
||||||
|
* "modifiedScore": 8.910,
|
||||||
|
* "modifyTime": "2025-06-25 10:00:00"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 实现逻辑:
|
||||||
|
* 1. 验证权限(只有裁判长可以修改)
|
||||||
|
* 2. 保存 originalScore(如果是第一次修改)
|
||||||
|
* 3. 更新 totalScore
|
||||||
|
* 4. 记录 modifyReason 和 modifyTime
|
||||||
|
*/
|
||||||
30
src/common/common.css
Normal file
30
src/common/common.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/* 全局样式 */
|
||||||
|
page {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
|
||||||
|
'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 防止系统样式影响 */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 清除默认样式 */
|
||||||
|
view, text, button, input {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
70
src/config/env.config.js
Normal file
70
src/config/env.config.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* 环境配置文件
|
||||||
|
* 控制应用的数据源模式(Mock数据 或 真实API)
|
||||||
|
*
|
||||||
|
* 使用说明:
|
||||||
|
* 1. Mock模式(UI演示、前端独立开发):设置 dataMode: 'mock'
|
||||||
|
* 2. API模式(真实数据对接):设置 dataMode: 'api'
|
||||||
|
* 3. 可在代码中动态切换模式
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ENV_CONFIG = {
|
||||||
|
// 开发环境配置
|
||||||
|
development: {
|
||||||
|
// 数据模式: 'mock' | 'api'
|
||||||
|
// mock - 使用本地Mock数据(保护UI版本)
|
||||||
|
// api - 调用真实后端接口
|
||||||
|
dataMode: 'api',
|
||||||
|
|
||||||
|
// API基础路径(dataMode为'api'时使用)
|
||||||
|
apiBaseURL: 'http://localhost:8123',
|
||||||
|
|
||||||
|
// 请求超时时间(毫秒)
|
||||||
|
timeout: 30000,
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// 测试环境配置
|
||||||
|
test: {
|
||||||
|
dataMode: 'api',
|
||||||
|
apiBaseURL: 'http://test-api.yourdomain.com',
|
||||||
|
debug: true,
|
||||||
|
timeout: 30000,
|
||||||
|
mockDelay: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
// 生产环境配置
|
||||||
|
production: {
|
||||||
|
dataMode: 'api',
|
||||||
|
apiBaseURL: 'https://api.yourdomain.com',
|
||||||
|
debug: false,
|
||||||
|
timeout: 30000,
|
||||||
|
mockDelay: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前环境(开发/测试/生产)
|
||||||
|
const env = process.env.NODE_ENV || 'development'
|
||||||
|
|
||||||
|
// 导出当前环境的配置
|
||||||
|
export default {
|
||||||
|
...ENV_CONFIG[env],
|
||||||
|
env
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速切换数据模式示例:
|
||||||
|
*
|
||||||
|
* // 在代码中使用
|
||||||
|
* import config from '@/config/env.config.js'
|
||||||
|
*
|
||||||
|
* if (config.dataMode === 'mock') {
|
||||||
|
* console.log('当前使用Mock数据')
|
||||||
|
* } else {
|
||||||
|
* console.log('当前使用真实API')
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 查看当前环境
|
||||||
|
* console.log('当前环境:', config.env)
|
||||||
|
* console.log('数据模式:', config.dataMode)
|
||||||
|
*/
|
||||||
11
src/main.js
Normal file
11
src/main.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
App.mpType = 'app'
|
||||||
|
|
||||||
|
const app = new Vue({
|
||||||
|
...App
|
||||||
|
})
|
||||||
|
app.$mount()
|
||||||
67
src/manifest.json
Normal file
67
src/manifest.json
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"name" : "武术评分系统",
|
||||||
|
"appid" : "",
|
||||||
|
"description" : "武术比赛评分系统",
|
||||||
|
"versionName" : "1.0.0",
|
||||||
|
"versionCode" : "100",
|
||||||
|
"transformPx" : false,
|
||||||
|
"app-plus" : {
|
||||||
|
"usingComponents" : true,
|
||||||
|
"nvueStyleCompiler" : "uni-app",
|
||||||
|
"compilerVersion" : 3,
|
||||||
|
"splashscreen" : {
|
||||||
|
"alwaysShowBeforeRender" : true,
|
||||||
|
"waiting" : true,
|
||||||
|
"autoclose" : true,
|
||||||
|
"delay" : 0
|
||||||
|
},
|
||||||
|
"modules" : {},
|
||||||
|
"distribute" : {
|
||||||
|
"android" : {
|
||||||
|
"permissions" : [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ios" : {},
|
||||||
|
"sdkConfigs" : {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"quickapp" : {},
|
||||||
|
"mp-weixin" : {
|
||||||
|
"appid" : "",
|
||||||
|
"setting" : {
|
||||||
|
"urlCheck" : false
|
||||||
|
},
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-alipay" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-baidu" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-toutiao" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"h5" : {
|
||||||
|
"title" : "武术评分系统",
|
||||||
|
"router" : {
|
||||||
|
"mode" : "hash",
|
||||||
|
"base" : "./"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
src/mock/athlete.js
Normal file
162
src/mock/athlete.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* Mock 数据 - 选手模块
|
||||||
|
* 模拟选手列表数据
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的选手列表(普通评委)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.judgeId - 评委ID
|
||||||
|
* @param {String} params.venueId - 场地ID
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Array} 选手列表(带评分状态)
|
||||||
|
*/
|
||||||
|
export function getMyAthletes(params) {
|
||||||
|
// 模拟3个选手数据
|
||||||
|
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',
|
||||||
|
name: '王五',
|
||||||
|
idCard: '123456789000000002',
|
||||||
|
team: '峨眉派武术学校',
|
||||||
|
number: '123-4567898277',
|
||||||
|
myScore: null, // 未评分
|
||||||
|
totalScore: null,
|
||||||
|
scored: false,
|
||||||
|
scoreTime: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选手列表(裁判长)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @param {String} params.venueId - 场地ID
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Array} 选手列表(带评分统计)
|
||||||
|
*/
|
||||||
|
export function getAthletesForAdmin(params) {
|
||||||
|
// 模拟5个选手数据
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
athleteId: '1',
|
||||||
|
name: '张三',
|
||||||
|
idCard: '123456789000000000',
|
||||||
|
team: '少林寺武术大学院',
|
||||||
|
number: '123-4567898275',
|
||||||
|
totalScore: 8.907,
|
||||||
|
judgeCount: 6, // 已评分评委数
|
||||||
|
totalJudges: 6, // 总评委数
|
||||||
|
canModify: true // 可以修改(所有评委已评分)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
athleteId: '2',
|
||||||
|
name: '李四',
|
||||||
|
idCard: '123456789000000001',
|
||||||
|
team: '武当山武术学院',
|
||||||
|
number: '123-4567898276',
|
||||||
|
totalScore: 8.902,
|
||||||
|
judgeCount: 6,
|
||||||
|
totalJudges: 6,
|
||||||
|
canModify: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
athleteId: '3',
|
||||||
|
name: '王五',
|
||||||
|
idCard: '123456789000000002',
|
||||||
|
team: '峨眉派武术学校',
|
||||||
|
number: '123-4567898277',
|
||||||
|
totalScore: null,
|
||||||
|
judgeCount: 3, // 只有3位评委评分
|
||||||
|
totalJudges: 6,
|
||||||
|
canModify: false // 不能修改(未全部评分)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
athleteId: '4',
|
||||||
|
name: '赵六',
|
||||||
|
idCard: '123456789000000003',
|
||||||
|
team: '华山武术学院',
|
||||||
|
number: '123-4567898278',
|
||||||
|
totalScore: 8.899,
|
||||||
|
judgeCount: 6,
|
||||||
|
totalJudges: 6,
|
||||||
|
canModify: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
athleteId: '5',
|
||||||
|
name: '孙七',
|
||||||
|
idCard: '123456789000000004',
|
||||||
|
team: '崆峒派武术学校',
|
||||||
|
number: '123-4567898279',
|
||||||
|
totalScore: 8.912,
|
||||||
|
judgeCount: 6,
|
||||||
|
totalJudges: 6,
|
||||||
|
canModify: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @returns {Array} 场地列表
|
||||||
|
*/
|
||||||
|
export function getVenues(params) {
|
||||||
|
return [
|
||||||
|
{ id: '1', name: '第一场地' },
|
||||||
|
{ id: '2', name: '第二场地' },
|
||||||
|
{ id: '3', name: '第三场地' },
|
||||||
|
{ id: '4', name: '第四场地' },
|
||||||
|
{ id: '5', name: '第五场地' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.competitionId - 比赛ID
|
||||||
|
* @returns {Array} 项目列表(对象数组,与API格式一致)
|
||||||
|
*/
|
||||||
|
export function getProjects(params) {
|
||||||
|
return [
|
||||||
|
{ id: '5', name: '女子组长拳' },
|
||||||
|
{ id: '6', name: '男子组陈氏太极拳' },
|
||||||
|
{ id: '7', name: '女子组双剑(含长穗双剑)' },
|
||||||
|
{ id: '8', name: '男子组杨氏太极拳' },
|
||||||
|
{ id: '9', name: '女子组刀术' },
|
||||||
|
{ id: '10', name: '男子组棍术' },
|
||||||
|
{ id: '11', name: '女子组枪术' },
|
||||||
|
{ id: '12', name: '男子组剑术' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getMyAthletes,
|
||||||
|
getAthletesForAdmin,
|
||||||
|
getVenues,
|
||||||
|
getProjects
|
||||||
|
}
|
||||||
117
src/mock/index.js
Normal file
117
src/mock/index.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* Mock数据中心
|
||||||
|
* 所有Mock数据的统一入口
|
||||||
|
*
|
||||||
|
* 这个文件汇总了所有业务模块的Mock数据函数,
|
||||||
|
* 提供给 dataAdapter.js 调用
|
||||||
|
*/
|
||||||
|
|
||||||
|
import loginMock from './login.js'
|
||||||
|
import athleteMock from './athlete.js'
|
||||||
|
import scoreMock from './score.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出所有Mock数据函数
|
||||||
|
*
|
||||||
|
* 资源名称(key)对应 dataAdapter.getData() 的第一个参数
|
||||||
|
* 例如:dataAdapter.getData('login', params) 会调用 loginMock.login(params)
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
// ==================== 认证模块 ====================
|
||||||
|
/**
|
||||||
|
* 登录验证
|
||||||
|
* @param {Object} params - { matchCode, inviteCode }
|
||||||
|
* @returns {Object} 用户信息和Token
|
||||||
|
*/
|
||||||
|
login: loginMock.login,
|
||||||
|
|
||||||
|
// ==================== 选手模块 ====================
|
||||||
|
/**
|
||||||
|
* 获取我的选手列表(普通评委)
|
||||||
|
* @param {Object} params - { judgeId, venueId, projectId }
|
||||||
|
* @returns {Array} 选手列表(带评分状态)
|
||||||
|
*/
|
||||||
|
getMyAthletes: athleteMock.getMyAthletes,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选手列表(裁判长)
|
||||||
|
* @param {Object} params - { competitionId, venueId, projectId }
|
||||||
|
* @returns {Array} 选手列表(带评分统计)
|
||||||
|
*/
|
||||||
|
getAthletesForAdmin: athleteMock.getAthletesForAdmin,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地列表
|
||||||
|
* @param {Object} params - { competitionId }
|
||||||
|
* @returns {Array} 场地列表
|
||||||
|
*/
|
||||||
|
getVenues: athleteMock.getVenues,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目列表
|
||||||
|
* @param {Object} params - { competitionId }
|
||||||
|
* @returns {Array} 项目列表
|
||||||
|
*/
|
||||||
|
getProjects: athleteMock.getProjects,
|
||||||
|
|
||||||
|
// ==================== 评分模块 ====================
|
||||||
|
/**
|
||||||
|
* 获取扣分项列表
|
||||||
|
* @param {Object} params - { projectId }
|
||||||
|
* @returns {Array} 扣分项列表
|
||||||
|
*/
|
||||||
|
getDeductions: scoreMock.getDeductions,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交评分
|
||||||
|
* @param {Object} params - { athleteId, judgeId, score, deductions, note }
|
||||||
|
* @returns {Object} 提交结果
|
||||||
|
*/
|
||||||
|
submitScore: scoreMock.submitScore,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评分详情(裁判长查看)
|
||||||
|
* @param {Object} params - { athleteId }
|
||||||
|
* @returns {Object} 评分详情(选手信息+评委评分)
|
||||||
|
*/
|
||||||
|
getScoreDetail: scoreMock.getScoreDetail,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改评分(裁判长)
|
||||||
|
* @param {Object} params - { athleteId, modifierId, modifiedScore, note }
|
||||||
|
* @returns {Object} 修改结果
|
||||||
|
*/
|
||||||
|
modifyScore: scoreMock.modifyScore
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用说明:
|
||||||
|
*
|
||||||
|
* 这个文件不直接在页面中使用,而是通过 dataAdapter.js 间接调用。
|
||||||
|
*
|
||||||
|
* 页面使用示例:
|
||||||
|
*
|
||||||
|
* import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
*
|
||||||
|
* // 登录
|
||||||
|
* const res = await dataAdapter.getData('login', {
|
||||||
|
* matchCode: '123',
|
||||||
|
* inviteCode: 'pub'
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // 获取选手列表
|
||||||
|
* const res = await dataAdapter.getData('getMyAthletes', {
|
||||||
|
* judgeId: '456',
|
||||||
|
* venueId: '1',
|
||||||
|
* projectId: '5'
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // 提交评分
|
||||||
|
* const res = await dataAdapter.getData('submitScore', {
|
||||||
|
* athleteId: '1',
|
||||||
|
* judgeId: '456',
|
||||||
|
* score: 8.907,
|
||||||
|
* deductions: [...],
|
||||||
|
* note: '表现优秀'
|
||||||
|
* })
|
||||||
|
*/
|
||||||
44
src/mock/login.js
Normal file
44
src/mock/login.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Mock 数据 - 登录模块
|
||||||
|
* 模拟登录验证和用户信息返回
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录验证
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.matchCode - 比赛编码
|
||||||
|
* @param {String} params.inviteCode - 邀请码(pub 或 admin)
|
||||||
|
* @returns {Object} 用户信息和Token
|
||||||
|
*/
|
||||||
|
export function login(params) {
|
||||||
|
const { matchCode, inviteCode } = params
|
||||||
|
|
||||||
|
// 模拟验证逻辑
|
||||||
|
const role = inviteCode.toLowerCase()
|
||||||
|
|
||||||
|
if (role !== 'pub' && role !== 'admin') {
|
||||||
|
throw new Error('邀请码错误,请使用 pub 或 admin')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回Mock登录数据
|
||||||
|
return {
|
||||||
|
token: 'mock_token_' + Date.now(),
|
||||||
|
userRole: role, // 'pub' 或 'admin'
|
||||||
|
matchId: '123',
|
||||||
|
matchName: '2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛',
|
||||||
|
matchTime: '2025年6月25日 9:00',
|
||||||
|
judgeId: '456',
|
||||||
|
judgeName: '欧阳丽娜',
|
||||||
|
// 普通评委有固定场地,裁判长可以查看所有场地
|
||||||
|
venueId: role === 'pub' ? '1' : null,
|
||||||
|
venueName: role === 'pub' ? '第一场地' : null,
|
||||||
|
// 分配的项目列表
|
||||||
|
projects: role === 'pub'
|
||||||
|
? ['女子组长拳', '男子组陈氏太极拳']
|
||||||
|
: ['女子组长拳', '男子组陈氏太极拳', '女子组双剑(含长穗双剑)', '男子组杨氏太极拳', '女子组刀术', '男子组棍术', '女子组枪术', '男子组剑术']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
login
|
||||||
|
}
|
||||||
162
src/mock/score.js
Normal file
162
src/mock/score.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* Mock 数据 - 评分模块
|
||||||
|
* 模拟评分相关数据
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扣分项列表
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.projectId - 项目ID
|
||||||
|
* @returns {Array} 扣分项列表
|
||||||
|
*/
|
||||||
|
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 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交评分
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.athleteId - 选手ID
|
||||||
|
* @param {String} params.judgeId - 评委ID
|
||||||
|
* @param {Number} params.score - 评分
|
||||||
|
* @param {Array} params.deductions - 扣分项
|
||||||
|
* @param {String} params.note - 备注
|
||||||
|
* @returns {Object} 提交结果
|
||||||
|
*/
|
||||||
|
export function submitScore(params) {
|
||||||
|
const { athleteId, judgeId, score, deductions, note } = params
|
||||||
|
|
||||||
|
// 模拟提交成功
|
||||||
|
console.log('Mock提交评分:', {
|
||||||
|
athleteId,
|
||||||
|
judgeId,
|
||||||
|
score,
|
||||||
|
deductions: deductions.filter(d => d.checked).length + '项',
|
||||||
|
note
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
scoreId: 'score_' + Date.now(),
|
||||||
|
athleteId,
|
||||||
|
judgeId,
|
||||||
|
score,
|
||||||
|
submitTime: new Date().toISOString(),
|
||||||
|
message: '评分提交成功'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评分详情(裁判长查看)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.athleteId - 选手ID
|
||||||
|
* @returns {Object} 评分详情
|
||||||
|
*/
|
||||||
|
export function getScoreDetail(params) {
|
||||||
|
const { athleteId } = params
|
||||||
|
|
||||||
|
// 模拟选手信息和评委评分
|
||||||
|
return {
|
||||||
|
athleteInfo: {
|
||||||
|
athleteId,
|
||||||
|
name: '张三',
|
||||||
|
idCard: '123456789000000000',
|
||||||
|
team: '少林寺武术大学院',
|
||||||
|
number: '123-4567898275',
|
||||||
|
totalScore: 8.907
|
||||||
|
},
|
||||||
|
// 6位评委的评分
|
||||||
|
judgeScores: [
|
||||||
|
{
|
||||||
|
judgeId: '1',
|
||||||
|
judgeName: '欧阳丽娜',
|
||||||
|
score: 8.907,
|
||||||
|
scoreTime: '2025-06-25 09:15:00',
|
||||||
|
note: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
judgeId: '2',
|
||||||
|
judgeName: '张三',
|
||||||
|
score: 8.901,
|
||||||
|
scoreTime: '2025-06-25 09:15:30',
|
||||||
|
note: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
judgeId: '3',
|
||||||
|
judgeName: '裁判姓名',
|
||||||
|
score: 8.902,
|
||||||
|
scoreTime: '2025-06-25 09:16:00',
|
||||||
|
note: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
judgeId: '4',
|
||||||
|
judgeName: '裁判姓名',
|
||||||
|
score: 8.907,
|
||||||
|
scoreTime: '2025-06-25 09:16:30',
|
||||||
|
note: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
judgeId: '5',
|
||||||
|
judgeName: '裁判姓名',
|
||||||
|
score: 8.905,
|
||||||
|
scoreTime: '2025-06-25 09:17:00',
|
||||||
|
note: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
judgeId: '6',
|
||||||
|
judgeName: '裁判姓名',
|
||||||
|
score: 8.904,
|
||||||
|
scoreTime: '2025-06-25 09:17:30',
|
||||||
|
note: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 修改记录(如果有)
|
||||||
|
modification: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改评分(裁判长)
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.athleteId - 选手ID
|
||||||
|
* @param {String} params.modifierId - 修改人ID(裁判长)
|
||||||
|
* @param {Number} params.modifiedScore - 修改后的分数
|
||||||
|
* @param {String} params.note - 修改原因
|
||||||
|
* @returns {Object} 修改结果
|
||||||
|
*/
|
||||||
|
export function modifyScore(params) {
|
||||||
|
const { athleteId, modifierId, modifiedScore, note } = params
|
||||||
|
|
||||||
|
// 模拟修改成功
|
||||||
|
console.log('Mock修改评分:', {
|
||||||
|
athleteId,
|
||||||
|
modifierId,
|
||||||
|
originalScore: 8.907,
|
||||||
|
modifiedScore,
|
||||||
|
note
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
athleteId,
|
||||||
|
originalScore: 8.907,
|
||||||
|
modifiedScore,
|
||||||
|
modifyTime: new Date().toISOString(),
|
||||||
|
message: '评分修改成功'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getDeductions,
|
||||||
|
submitScore,
|
||||||
|
getScoreDetail,
|
||||||
|
modifyScore
|
||||||
|
}
|
||||||
45
src/pages.json
Normal file
45
src/pages.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/login/login",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/score-list/score-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/modify-score/modify-score",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/score-list-multi/score-list-multi",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/score-detail/score-detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationBarTextStyle": "white",
|
||||||
|
"navigationBarTitleText": "评分系统",
|
||||||
|
"navigationBarBackgroundColor": "#1B7C5E",
|
||||||
|
"backgroundColor": "#F5F5F5"
|
||||||
|
}
|
||||||
|
}
|
||||||
303
src/pages/login/login.vue
Normal file
303
src/pages/login/login.vue
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
<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="content">
|
||||||
|
<view class="page-title">进入评分</view>
|
||||||
|
|
||||||
|
<!-- 比赛编码输入 -->
|
||||||
|
<view class="input-group">
|
||||||
|
<view class="input-label">比赛编码</view>
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入比赛编码"
|
||||||
|
v-model="matchCode"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 评委邀请码输入 -->
|
||||||
|
<view class="input-group">
|
||||||
|
<view class="input-label">评委邀请码</view>
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入评委邀请码"
|
||||||
|
v-model="inviteCode"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 立即评分按钮 -->
|
||||||
|
<button class="submit-btn" @click="handleSubmit">立即评分</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
import config from '@/config/env.config.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
matchCode: '',
|
||||||
|
inviteCode: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
// 开发环境显示当前数据模式
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('='.repeat(50))
|
||||||
|
console.log('当前数据模式:', config.dataMode)
|
||||||
|
console.log('Mock模式:', dataAdapter.isMockMode() ? '是' : '否')
|
||||||
|
console.log('API模式:', dataAdapter.isApiMode() ? '是' : '否')
|
||||||
|
console.log('='.repeat(50))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleSubmit() {
|
||||||
|
// 表单验证
|
||||||
|
if (!this.matchCode) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入比赛编码',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.inviteCode) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入评委邀请码',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 显示加载
|
||||||
|
uni.showLoading({
|
||||||
|
title: '登录中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 进行登录
|
||||||
|
// Mock模式:调用 mock/login.js 的 login 函数
|
||||||
|
// API模式:调用 api/auth.js 的 login 函数(POST /api/mini/login)
|
||||||
|
const response = await dataAdapter.getData('login', {
|
||||||
|
matchCode: this.matchCode,
|
||||||
|
inviteCode: this.inviteCode
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 处理登录响应(Mock和API返回格式相同)
|
||||||
|
const {
|
||||||
|
token,
|
||||||
|
userRole,
|
||||||
|
matchId,
|
||||||
|
matchName,
|
||||||
|
matchTime,
|
||||||
|
judgeId,
|
||||||
|
judgeName,
|
||||||
|
venueId,
|
||||||
|
venueName,
|
||||||
|
projects
|
||||||
|
} = response.data
|
||||||
|
|
||||||
|
// 保存Token到本地存储
|
||||||
|
uni.setStorageSync('token', token)
|
||||||
|
|
||||||
|
// 保存用户信息到全局数据
|
||||||
|
getApp().globalData = {
|
||||||
|
userRole, // 'pub' 或 'admin'
|
||||||
|
matchCode: this.matchCode,
|
||||||
|
matchId,
|
||||||
|
matchName,
|
||||||
|
matchTime,
|
||||||
|
judgeId,
|
||||||
|
judgeName,
|
||||||
|
venueId, // 普通评委有场地,裁判长为null
|
||||||
|
venueName,
|
||||||
|
projects, // 分配的项目列表
|
||||||
|
currentProjectIndex: 0 // 当前选中的项目索引
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('登录成功:', {
|
||||||
|
userRole,
|
||||||
|
judgeName,
|
||||||
|
venueId: venueId || '全部场地',
|
||||||
|
projects: projects.length + '个项目'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示登录成功提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
|
||||||
|
// 根据角色跳转到不同页面
|
||||||
|
setTimeout(() => {
|
||||||
|
if (userRole === 'admin') {
|
||||||
|
// 裁判长跳转到多场地列表页(可以修改评分)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/score-list-multi/score-list-multi'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 普通裁判跳转到评分列表页(可以评分)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/score-list/score-list'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 错误处理
|
||||||
|
console.error('登录失败:', error)
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '登录失败,请重试',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导航栏 */
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主体内容 */
|
||||||
|
.content {
|
||||||
|
padding: 60rpx 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入组 */
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field::placeholder {
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-tip {
|
||||||
|
position: absolute;
|
||||||
|
right: 30rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提交按钮 */
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 90rpx;
|
||||||
|
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 80rpx;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(27, 124, 94, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
588
src/pages/modify-score/modify-score.vue
Normal file
588
src/pages/modify-score/modify-score.vue
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 自定义导航栏 -->
|
||||||
|
<view class="nav-bar">
|
||||||
|
<view class="nav-left" @click="goBack">
|
||||||
|
<text class="back-icon">‹</text>
|
||||||
|
</view>
|
||||||
|
<view class="nav-title">修改评分</view>
|
||||||
|
<view class="nav-right">
|
||||||
|
<view class="icon-menu">···</view>
|
||||||
|
<view class="icon-close">⊗</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选手信息 -->
|
||||||
|
<view class="player-info-section">
|
||||||
|
<view class="player-header">
|
||||||
|
<view class="player-name">{{ athleteInfo.name }}</view>
|
||||||
|
<view class="total-score-label">
|
||||||
|
<text class="label-text">总分:</text>
|
||||||
|
<text class="score-value">{{ athleteInfo.totalScore }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="player-details">
|
||||||
|
<view class="detail-item">身份证:{{ athleteInfo.idCard }}</view>
|
||||||
|
<view class="detail-item">队伍:{{ athleteInfo.team }}</view>
|
||||||
|
<view class="detail-item">编号:{{ athleteInfo.number }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 评委评分统计 -->
|
||||||
|
<view class="judges-section">
|
||||||
|
<view class="section-title">共有{{ judgeScores.length }}位评委完成评分</view>
|
||||||
|
<view class="judges-scores">
|
||||||
|
<view
|
||||||
|
class="judge-score-item"
|
||||||
|
v-for="judge in judgeScores"
|
||||||
|
:key="judge.judgeId"
|
||||||
|
>
|
||||||
|
<text class="judge-name">{{ judge.judgeName }}:</text>
|
||||||
|
<text class="judge-score">{{ judge.score }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 修改总分区域 -->
|
||||||
|
<view class="modify-section">
|
||||||
|
<view class="modify-header">
|
||||||
|
<text class="modify-label">修改总分(+-0.005分)</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="score-control">
|
||||||
|
<view class="control-btn decrease" @click="decreaseScore">
|
||||||
|
<text class="btn-symbol">-</text>
|
||||||
|
<text class="btn-value">-0.001</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="score-display">
|
||||||
|
<text class="current-score">{{ currentScore.toFixed(3) }}</text>
|
||||||
|
<text class="no-modify-text">可不改</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="control-btn increase" @click="increaseScore">
|
||||||
|
<text class="btn-symbol">+</text>
|
||||||
|
<text class="btn-value">+0.001</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- <view class="modify-tip">
|
||||||
|
裁判长修改:保留3位小数点,超过上限或下限时,按钮置灰
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 备注 -->
|
||||||
|
<view class="note-section">
|
||||||
|
<view class="note-label">
|
||||||
|
<text>备注:</text>
|
||||||
|
</view>
|
||||||
|
<view class="note-input-wrapper">
|
||||||
|
<textarea
|
||||||
|
class="note-input"
|
||||||
|
placeholder="请输入修改备注"
|
||||||
|
v-model="note"
|
||||||
|
maxlength="200"
|
||||||
|
/>
|
||||||
|
<text class="optional-text">可不填</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 修改按钮 -->
|
||||||
|
<button class="modify-btn" @click="handleModify">修改</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
import config from '@/config/env.config.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
athleteInfo: {
|
||||||
|
athleteId: '',
|
||||||
|
name: '',
|
||||||
|
idCard: '',
|
||||||
|
team: '',
|
||||||
|
number: '',
|
||||||
|
totalScore: 0
|
||||||
|
},
|
||||||
|
judgeScores: [],
|
||||||
|
modification: null,
|
||||||
|
modifierId: '',
|
||||||
|
currentScore: 8.000,
|
||||||
|
originalScore: 8.000,
|
||||||
|
note: '',
|
||||||
|
minScore: 5.0,
|
||||||
|
maxScore: 10.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
// 获取全局数据
|
||||||
|
const app = getApp()
|
||||||
|
const globalData = app.globalData || {}
|
||||||
|
|
||||||
|
// 获取当前选手信息(从 score-list-multi 页面传递)
|
||||||
|
const currentAthlete = globalData.currentAthlete || {}
|
||||||
|
|
||||||
|
// 获取裁判长ID
|
||||||
|
this.modifierId = globalData.judgeId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('修改评分页加载:', {
|
||||||
|
athleteId: currentAthlete.athleteId,
|
||||||
|
modifierId: this.modifierId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载选手评分详情
|
||||||
|
if (currentAthlete.athleteId) {
|
||||||
|
await this.loadScoreDetail(currentAthlete.athleteId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadScoreDetail(athleteId) {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取评分详情
|
||||||
|
// Mock模式:调用 mock/score.js 的 getScoreDetail 函数
|
||||||
|
// API模式:调用 api/score.js 的 getScoreDetail 函数(GET /api/mini/score/detail/{athleteId})
|
||||||
|
const response = await dataAdapter.getData('getScoreDetail', {
|
||||||
|
athleteId: athleteId
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 保存选手信息和评分详情
|
||||||
|
this.athleteInfo = response.data.athleteInfo || {}
|
||||||
|
this.judgeScores = response.data.judgeScores || []
|
||||||
|
this.modification = response.data.modification || null
|
||||||
|
|
||||||
|
// 设置初始分数
|
||||||
|
this.originalScore = this.athleteInfo.totalScore || 8.000
|
||||||
|
this.currentScore = this.originalScore
|
||||||
|
|
||||||
|
// 如果之前已修改过,加载修改后的分数
|
||||||
|
if (this.modification && this.modification.modifiedScore) {
|
||||||
|
this.currentScore = this.modification.modifiedScore
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('评分详情加载成功:', {
|
||||||
|
athlete: this.athleteInfo,
|
||||||
|
judges: this.judgeScores.length,
|
||||||
|
originalScore: this.originalScore,
|
||||||
|
currentScore: this.currentScore,
|
||||||
|
modification: this.modification
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载评分详情失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
uni.navigateBack()
|
||||||
|
},
|
||||||
|
|
||||||
|
decreaseScore() {
|
||||||
|
if (this.currentScore > this.minScore) {
|
||||||
|
this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
increaseScore() {
|
||||||
|
if (this.currentScore < this.maxScore) {
|
||||||
|
this.currentScore = parseFloat((this.currentScore + 0.001).toFixed(3))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleModify() {
|
||||||
|
// 验证评分范围
|
||||||
|
if (this.currentScore < this.minScore || this.currentScore > this.maxScore) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `评分必须在${this.minScore}-${this.maxScore}分之间`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有修改
|
||||||
|
if (this.currentScore === this.originalScore && !this.note) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请修改分数或填写备注',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '提交中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 修改评分
|
||||||
|
// Mock模式:调用 mock/score.js 的 modifyScore 函数
|
||||||
|
// API模式:调用 api/score.js 的 modifyScore 函数(PUT /api/mini/score/modify)
|
||||||
|
const response = await dataAdapter.getData('modifyScore', {
|
||||||
|
athleteId: this.athleteInfo.athleteId,
|
||||||
|
modifierId: this.modifierId,
|
||||||
|
modifiedScore: this.currentScore,
|
||||||
|
note: this.note
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('修改评分成功:', {
|
||||||
|
athleteId: this.athleteInfo.athleteId,
|
||||||
|
originalScore: this.originalScore,
|
||||||
|
modifiedScore: this.currentScore,
|
||||||
|
note: this.note,
|
||||||
|
response: response
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '修改成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('修改评分失败:', error)
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '修改失败,请重试',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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-left {
|
||||||
|
position: absolute;
|
||||||
|
left: 30rpx;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-icon {
|
||||||
|
font-size: 60rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选手信息 */
|
||||||
|
.player-info-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
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: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-score-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 评委评分统计 */
|
||||||
|
.judges-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.judges-scores {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.judge-score-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.judge-name {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.judge-score {
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改总分区域 */
|
||||||
|
.modify-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-header {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease {
|
||||||
|
background-color: #FFE5E5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase {
|
||||||
|
background-color: #E5F5F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-symbol {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease .btn-symbol {
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase .btn-symbol {
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-value {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease .btn-value {
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase .btn-value {
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-display {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-score {
|
||||||
|
font-size: 60rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-modify-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-tip {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
line-height: 1.6;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 备注 */
|
||||||
|
.note-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input-wrapper {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input::placeholder {
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optional-text {
|
||||||
|
position: absolute;
|
||||||
|
right: 30rpx;
|
||||||
|
bottom: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改按钮 */
|
||||||
|
.modify-btn {
|
||||||
|
margin: 30rpx;
|
||||||
|
height: 90rpx;
|
||||||
|
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(27, 124, 94, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-btn:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
581
src/pages/score-detail/score-detail.vue
Normal file
581
src/pages/score-detail/score-detail.vue
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 自定义导航栏 -->
|
||||||
|
<view class="nav-bar">
|
||||||
|
<view class="nav-left" @click="goBack">
|
||||||
|
<text class="back-icon">‹</text>
|
||||||
|
</view>
|
||||||
|
<view class="nav-title">评分详情</view>
|
||||||
|
<view class="nav-right">
|
||||||
|
<view class="icon-menu">···</view>
|
||||||
|
<view class="icon-close">⊗</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选手信息 -->
|
||||||
|
<view class="player-info-section">
|
||||||
|
<view class="player-name">{{ player.name }}</view>
|
||||||
|
<view class="player-details">
|
||||||
|
<view class="detail-item">身份证:{{ player.idCard }}</view>
|
||||||
|
<view class="detail-item">队伍:{{ player.team }}</view>
|
||||||
|
<view class="detail-item">编号:{{ player.number }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 评分提示 -->
|
||||||
|
<view class="score-tip">
|
||||||
|
点击分数填写或拖动滑块打分(5-10分)
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分数调整 -->
|
||||||
|
<view class="score-control">
|
||||||
|
<view class="control-btn decrease" @click="decreaseScore">
|
||||||
|
<text class="btn-symbol">-</text>
|
||||||
|
<!-- <text class="btn-value">-0.001</text> -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="score-display">
|
||||||
|
<text class="current-score">{{ currentScore.toFixed(3) }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="control-btn increase" @click="increaseScore">
|
||||||
|
<text class="btn-symbol">+</text>
|
||||||
|
<!-- <text class="btn-value">+0.001</text> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- <view class="judge-tip">
|
||||||
|
裁判评分:保留3位小数点,超过上限或下限时,按钮置灰
|
||||||
|
</view> -->
|
||||||
|
|
||||||
|
<!-- 扣分项 -->
|
||||||
|
<view class="deduction-section">
|
||||||
|
<view class="deduction-header">
|
||||||
|
<text class="deduction-label">扣分项:</text>
|
||||||
|
<!-- <text class="deduction-hint">扣分项多选</text> -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="deduction-list">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in deductions"
|
||||||
|
:key="item.deductionId"
|
||||||
|
class="deduction-item"
|
||||||
|
@click="toggleDeduction(index)"
|
||||||
|
>
|
||||||
|
<view :class="['checkbox', item.checked ? 'checked' : '']">
|
||||||
|
<text v-if="item.checked" class="check-icon">✓</text>
|
||||||
|
</view>
|
||||||
|
<text class="deduction-text">{{ item.deductionName }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 备注 -->
|
||||||
|
<view class="note-section">
|
||||||
|
<view class="note-label">
|
||||||
|
<text>备注:</text>
|
||||||
|
</view>
|
||||||
|
<view class="note-input-wrapper">
|
||||||
|
<textarea
|
||||||
|
class="note-input"
|
||||||
|
placeholder="请输入修改备注"
|
||||||
|
v-model="note"
|
||||||
|
maxlength="200"
|
||||||
|
/>
|
||||||
|
<!-- <text class="optional-text">可不填</text> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交按钮 -->
|
||||||
|
<button class="submit-btn" @click="handleSubmit">提交</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
import config from '@/config/env.config.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
player: {
|
||||||
|
athleteId: '',
|
||||||
|
name: '',
|
||||||
|
idCard: '',
|
||||||
|
team: '',
|
||||||
|
number: ''
|
||||||
|
},
|
||||||
|
judgeId: '',
|
||||||
|
projectId: '',
|
||||||
|
currentScore: 8.000,
|
||||||
|
note: '',
|
||||||
|
minScore: 5.0,
|
||||||
|
maxScore: 10.0,
|
||||||
|
deductions: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
// 获取全局数据
|
||||||
|
const app = getApp()
|
||||||
|
const globalData = app.globalData || {}
|
||||||
|
|
||||||
|
// 加载当前选手信息(从 score-list 页面传递)
|
||||||
|
const currentAthlete = globalData.currentAthlete || {}
|
||||||
|
this.player = {
|
||||||
|
athleteId: currentAthlete.athleteId || '',
|
||||||
|
name: currentAthlete.name || '选手姓名',
|
||||||
|
idCard: currentAthlete.idCard || '',
|
||||||
|
team: currentAthlete.team || '',
|
||||||
|
number: currentAthlete.number || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果选手已评分,加载其原有评分
|
||||||
|
if (currentAthlete.scored && currentAthlete.myScore) {
|
||||||
|
this.currentScore = currentAthlete.myScore
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载评委ID和项目ID
|
||||||
|
this.judgeId = globalData.judgeId
|
||||||
|
const projects = globalData.projects || []
|
||||||
|
const currentIndex = globalData.currentProjectIndex || 0
|
||||||
|
const currentProject = projects[currentIndex] || {}
|
||||||
|
this.projectId = currentProject.projectId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('评分详情页加载:', {
|
||||||
|
athlete: this.player,
|
||||||
|
judgeId: this.judgeId,
|
||||||
|
projectId: this.projectId,
|
||||||
|
initialScore: this.currentScore
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载扣分项列表
|
||||||
|
await this.loadDeductions()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadDeductions() {
|
||||||
|
try {
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取扣分项列表
|
||||||
|
// Mock模式:调用 mock/score.js 的 getDeductions 函数
|
||||||
|
// API模式:调用 api/score.js 的 getDeductions 函数(GET /martial/deductionItem/list)
|
||||||
|
const response = await dataAdapter.getData('getDeductions', {
|
||||||
|
projectId: this.projectId
|
||||||
|
})
|
||||||
|
|
||||||
|
// 为每个扣分项添加 checked 状态
|
||||||
|
this.deductions = (response.data || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
checked: false
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('扣分项加载成功:', this.deductions)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载扣分项失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载扣分项失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
uni.navigateBack()
|
||||||
|
},
|
||||||
|
|
||||||
|
decreaseScore() {
|
||||||
|
if (this.currentScore > this.minScore) {
|
||||||
|
this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
increaseScore() {
|
||||||
|
if (this.currentScore < this.maxScore) {
|
||||||
|
this.currentScore = parseFloat((this.currentScore + 0.001).toFixed(3))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleDeduction(index) {
|
||||||
|
this.deductions[index].checked = !this.deductions[index].checked
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleSubmit() {
|
||||||
|
// 验证评分范围
|
||||||
|
if (this.currentScore < this.minScore || this.currentScore > this.maxScore) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `评分必须在${this.minScore}-${this.maxScore}分之间`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集选中的扣分项
|
||||||
|
const selectedDeductions = this.deductions
|
||||||
|
.filter(item => item.checked)
|
||||||
|
.map(item => ({
|
||||||
|
deductionId: item.deductionId,
|
||||||
|
deductionName: item.deductionName,
|
||||||
|
deductionScore: item.deductionScore
|
||||||
|
}))
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '提交中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 提交评分
|
||||||
|
// Mock模式:调用 mock/score.js 的 submitScore 函数
|
||||||
|
// API模式:调用 api/score.js 的 submitScore 函数(POST /martial/score/submit)
|
||||||
|
const response = await dataAdapter.getData('submitScore', {
|
||||||
|
athleteId: this.player.athleteId,
|
||||||
|
judgeId: this.judgeId,
|
||||||
|
score: this.currentScore,
|
||||||
|
deductions: selectedDeductions,
|
||||||
|
note: this.note
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('评分提交成功:', {
|
||||||
|
athleteId: this.player.athleteId,
|
||||||
|
score: this.currentScore,
|
||||||
|
deductions: selectedDeductions,
|
||||||
|
response: response
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '提交成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('提交评分失败:', error)
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '提交失败,请重试',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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-left {
|
||||||
|
position: absolute;
|
||||||
|
left: 30rpx;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-icon {
|
||||||
|
font-size: 60rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选手信息 */
|
||||||
|
.player-info-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #CD8B6F;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 评分提示 */
|
||||||
|
.score-tip {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分数控制 */
|
||||||
|
.score-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 60rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease {
|
||||||
|
background-color: #FFE5E5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase {
|
||||||
|
background-color: #E5F5F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-symbol {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease .btn-symbol {
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase .btn-symbol {
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-value {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.decrease .btn-value {
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn.increase .btn-value {
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-display {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-score {
|
||||||
|
font-size: 80rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.judge-tip {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 扣分项 */
|
||||||
|
.deduction-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-hint {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border: 2rpx solid #CCCCCC;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox.checked {
|
||||||
|
background-color: #1B7C5E;
|
||||||
|
border-color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-icon {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.4;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 备注 */
|
||||||
|
.note-section {
|
||||||
|
margin: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input-wrapper {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-input::placeholder {
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optional-text {
|
||||||
|
position: absolute;
|
||||||
|
right: 30rpx;
|
||||||
|
bottom: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提交按钮 */
|
||||||
|
.submit-btn {
|
||||||
|
margin: 30rpx;
|
||||||
|
height: 90rpx;
|
||||||
|
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(27, 124, 94, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
547
src/pages/score-list-multi/score-list-multi.vue
Normal file
547
src/pages/score-list-multi/score-list-multi.vue
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
<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">
|
||||||
|
<!-- 场地切换 - 横向滚动 -->
|
||||||
|
<scroll-view class="venue-scroll" scroll-x="true" show-scrollbar="false">
|
||||||
|
<view class="venue-tabs">
|
||||||
|
<view
|
||||||
|
v-for="venue in venues"
|
||||||
|
:key="venue.venueId"
|
||||||
|
:class="['venue-tab', currentVenue === venue.venueId ? 'active' : '']"
|
||||||
|
@click="switchVenue(venue.venueId)"
|
||||||
|
>
|
||||||
|
{{ venue.venueName }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view class="venue-tip">
|
||||||
|
<!-- <text class="tip-bold">裁判长可看见所有场地和项目</text> -->
|
||||||
|
<!-- <text class="tip-normal">(场地和项目可动态全部),可以点击切换</text> -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 项目选择 - 横向滚动 -->
|
||||||
|
<scroll-view class="project-scroll" scroll-x="true" show-scrollbar="false">
|
||||||
|
<view class="project-list">
|
||||||
|
<view
|
||||||
|
v-for="(project, index) in projects"
|
||||||
|
:key="project.projectId"
|
||||||
|
:class="['project-btn', currentProject === project.projectId ? 'active' : '']"
|
||||||
|
@click="switchProject(project.projectId)"
|
||||||
|
>
|
||||||
|
{{ project.projectName }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-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"
|
||||||
|
>
|
||||||
|
<view class="player-header">
|
||||||
|
<view class="player-name">{{ player.name }}</view>
|
||||||
|
|
||||||
|
<!-- 已评分:显示总分和修改按钮 -->
|
||||||
|
<view class="action-area" v-if="player.totalScore">
|
||||||
|
<text class="total-score">总分:{{ player.totalScore }}</text>
|
||||||
|
<view class="chief-actions">
|
||||||
|
<!-- <text class="chief-hint">裁判长功能:修改评分、修改按钮需等总分出来才出现</text> -->
|
||||||
|
<button class="modify-btn" @click="goToModify(player)">修改</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="player-info">
|
||||||
|
<view class="info-item">身份证:{{ 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: {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
time: ''
|
||||||
|
},
|
||||||
|
competitionId: '',
|
||||||
|
currentVenue: '',
|
||||||
|
currentProject: '',
|
||||||
|
venues: [],
|
||||||
|
projects: [],
|
||||||
|
players: [],
|
||||||
|
scoredCount: 0,
|
||||||
|
totalCount: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
// 获取全局数据
|
||||||
|
const app = getApp()
|
||||||
|
const globalData = app.globalData || {}
|
||||||
|
|
||||||
|
// 加载比赛信息
|
||||||
|
this.matchInfo = {
|
||||||
|
id: globalData.matchId,
|
||||||
|
name: globalData.matchName || '比赛名称',
|
||||||
|
time: globalData.matchTime || '比赛时间'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:裁判长没有固定场地和项目,需要查看所有
|
||||||
|
this.competitionId = globalData.matchId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('裁判长列表页加载:', {
|
||||||
|
userRole: globalData.userRole,
|
||||||
|
competitionId: this.competitionId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载场地和项目列表
|
||||||
|
await this.loadVenuesAndProjects()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadVenuesAndProjects() {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取场地列表
|
||||||
|
// Mock模式:调用 mock/athlete.js 的 getVenues 函数
|
||||||
|
// API模式:调用 api/athlete.js 的 getVenues 函数(GET /martial/venue/list)
|
||||||
|
const venuesRes = await dataAdapter.getData('getVenues', {
|
||||||
|
competitionId: this.competitionId
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取项目列表
|
||||||
|
// Mock模式:调用 mock/athlete.js 的 getProjects 函数
|
||||||
|
// API模式:调用 api/athlete.js 的 getProjects 函数(GET /martial/project/list)
|
||||||
|
const projectsRes = await dataAdapter.getData('getProjects', {
|
||||||
|
competitionId: this.competitionId
|
||||||
|
})
|
||||||
|
|
||||||
|
this.venues = venuesRes.data || []
|
||||||
|
this.projects = projectsRes.data || []
|
||||||
|
|
||||||
|
// 默认选中第一个场地和项目
|
||||||
|
if (this.venues.length > 0) {
|
||||||
|
this.currentVenue = this.venues[0].venueId
|
||||||
|
}
|
||||||
|
if (this.projects.length > 0) {
|
||||||
|
this.currentProject = this.projects[0].projectId
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('场地和项目加载成功:', {
|
||||||
|
venues: this.venues.length,
|
||||||
|
projects: this.projects.length,
|
||||||
|
currentVenue: this.currentVenue,
|
||||||
|
currentProject: this.currentProject
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载选手列表
|
||||||
|
if (this.currentVenue && this.currentProject) {
|
||||||
|
await this.loadPlayers()
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载场地和项目失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadPlayers() {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取选手列表(裁判长视图)
|
||||||
|
// Mock模式:调用 mock/athlete.js 的 getAthletesForAdmin 函数
|
||||||
|
// API模式:调用 api/athlete.js 的 getAthletesForAdmin 函数(GET /api/mini/athletes/admin)
|
||||||
|
const response = await dataAdapter.getData('getAthletesForAdmin', {
|
||||||
|
competitionId: this.competitionId,
|
||||||
|
venueId: this.currentVenue,
|
||||||
|
projectId: this.currentProject
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 保存选手列表
|
||||||
|
this.players = response.data || []
|
||||||
|
|
||||||
|
// 计算评分统计(裁判长视图:统计有总分的选手)
|
||||||
|
this.totalCount = this.players.length
|
||||||
|
this.scoredCount = this.players.filter(p => p.totalScore).length
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('选手列表加载成功:', {
|
||||||
|
venueId: this.currentVenue,
|
||||||
|
projectId: this.currentProject,
|
||||||
|
total: this.totalCount,
|
||||||
|
scored: this.scoredCount,
|
||||||
|
players: this.players
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载选手列表失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async switchVenue(venueId) {
|
||||||
|
if (this.currentVenue === venueId) return
|
||||||
|
|
||||||
|
this.currentVenue = venueId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('切换场地:', venueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新加载选手列表
|
||||||
|
await this.loadPlayers()
|
||||||
|
},
|
||||||
|
|
||||||
|
async switchProject(projectId) {
|
||||||
|
if (this.currentProject === projectId) return
|
||||||
|
|
||||||
|
this.currentProject = projectId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('切换项目:', projectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新加载选手列表
|
||||||
|
await this.loadPlayers()
|
||||||
|
},
|
||||||
|
|
||||||
|
goToModify(player) {
|
||||||
|
// 保存当前选手信息到全局数据
|
||||||
|
const app = getApp()
|
||||||
|
app.globalData.currentAthlete = player
|
||||||
|
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/modify-score/modify-score'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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-scroll {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tabs {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 30rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 4rpx solid #E0E0E0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tab {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666666;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tab.active {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tab.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -24rpx;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4rpx;
|
||||||
|
background-color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tip {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-bold {
|
||||||
|
color: #FF4D6A;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-normal {
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 项目滚动容器 */
|
||||||
|
.project-scroll {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-list {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-btn {
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 2rpx solid #CCCCCC;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-btn.active {
|
||||||
|
background-color: #1B7C5E;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border-color: #1B7C5E;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 评分统计 */
|
||||||
|
.score-stats {
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-text {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-number {
|
||||||
|
color: #1B7C5E;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选手列表 */
|
||||||
|
.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: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-score {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chief-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chief-hint {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1.5;
|
||||||
|
max-width: 400rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-btn {
|
||||||
|
padding: 12rpx 40rpx;
|
||||||
|
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modify-btn:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
441
src/pages/score-list/score-list.vue
Normal file
441
src/pages/score-list/score-list.vue
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
<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 active">{{ venueInfo.name }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="project-section">
|
||||||
|
<view class="project-btn active">{{ projectInfo.name }}</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"
|
||||||
|
>
|
||||||
|
<view class="player-header">
|
||||||
|
<view class="player-name">{{ player.name }}</view>
|
||||||
|
|
||||||
|
<!-- 已评分:显示我的评分和总分 -->
|
||||||
|
<view class="player-scores" v-if="player.scored">
|
||||||
|
<text class="my-score">我的评分:{{ player.myScore }}</text>
|
||||||
|
<text class="total-score">总分:{{ player.totalScore }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 未评分:显示评分按钮 -->
|
||||||
|
<button
|
||||||
|
class="score-btn"
|
||||||
|
v-else
|
||||||
|
@click="goToScoreDetail(player)"
|
||||||
|
>
|
||||||
|
评分
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="player-info">
|
||||||
|
<view class="info-item">身份证:{{ 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: '',
|
||||||
|
players: [],
|
||||||
|
scoredCount: 0,
|
||||||
|
totalCount: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onLoad() {
|
||||||
|
// 获取全局数据
|
||||||
|
const app = getApp()
|
||||||
|
const globalData = app.globalData || {}
|
||||||
|
|
||||||
|
// 加载比赛信息
|
||||||
|
this.matchInfo = {
|
||||||
|
name: globalData.matchName || '比赛名称',
|
||||||
|
time: globalData.matchTime || '比赛时间'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载场地信息
|
||||||
|
this.venueInfo = {
|
||||||
|
id: globalData.venueId,
|
||||||
|
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.judgeId = globalData.judgeId
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log('评分列表页加载:', {
|
||||||
|
judgeId: this.judgeId,
|
||||||
|
venueId: this.venueInfo.id,
|
||||||
|
projectId: this.projectInfo.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载选手列表
|
||||||
|
await this.loadPlayers()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadPlayers() {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 🔥 关键改动:使用 dataAdapter 获取选手列表
|
||||||
|
// Mock模式:调用 mock/athlete.js 的 getMyAthletes 函数
|
||||||
|
// API模式:调用 api/athlete.js 的 getMyAthletes 函数(GET /api/mini/athletes)
|
||||||
|
const response = await dataAdapter.getData('getMyAthletes', {
|
||||||
|
judgeId: this.judgeId,
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goToScoreDetail(player) {
|
||||||
|
// 保存当前选手信息到全局数据
|
||||||
|
const app = getApp()
|
||||||
|
app.globalData.currentAthlete = player
|
||||||
|
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/score-detail/score-detail'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 4rpx solid #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tab {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-tab.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -24rpx;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4rpx;
|
||||||
|
background-color: #1B7C5E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-hint {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #FF4D6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-btn {
|
||||||
|
padding: 20rpx 40rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 2rpx solid #1B7C5E;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #1B7C5E;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
0
src/static/.gitkeep
Normal file
0
src/static/.gitkeep
Normal file
73
src/uni.scss
Normal file
73
src/uni.scss
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* 这里是uni-app内置的常用样式变量
|
||||||
|
*
|
||||||
|
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
|
||||||
|
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
|
||||||
|
*
|
||||||
|
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 颜色变量 */
|
||||||
|
|
||||||
|
/* 主色 */
|
||||||
|
$uni-color-primary: #1B7C5E;
|
||||||
|
$uni-color-success: #2A9D7E;
|
||||||
|
$uni-color-warning: #f0ad4e;
|
||||||
|
$uni-color-error: #FF4D6A;
|
||||||
|
|
||||||
|
/* 文字基本颜色 */
|
||||||
|
$uni-text-color: #333;
|
||||||
|
$uni-text-color-inverse: #fff;
|
||||||
|
$uni-text-color-grey: #999;
|
||||||
|
$uni-text-color-placeholder: #CCCCCC;
|
||||||
|
$uni-text-color-disable: #c0c0c0;
|
||||||
|
|
||||||
|
/* 背景颜色 */
|
||||||
|
$uni-bg-color: #F5F5F5;
|
||||||
|
$uni-bg-color-grey: #f8f8f8;
|
||||||
|
$uni-bg-color-hover: #f1f1f1;
|
||||||
|
$uni-bg-color-mask: rgba(0, 0, 0, 0.4);
|
||||||
|
|
||||||
|
/* 边框颜色 */
|
||||||
|
$uni-border-color: #e5e5e5;
|
||||||
|
|
||||||
|
/* 尺寸变量 */
|
||||||
|
|
||||||
|
/* 文字尺寸 */
|
||||||
|
$uni-font-size-sm: 24rpx;
|
||||||
|
$uni-font-size-base: 28rpx;
|
||||||
|
$uni-font-size-lg: 32rpx;
|
||||||
|
|
||||||
|
/* 图片尺寸 */
|
||||||
|
$uni-img-size-sm: 40rpx;
|
||||||
|
$uni-img-size-base: 52rpx;
|
||||||
|
$uni-img-size-lg: 80rpx;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
$uni-border-radius-sm: 4rpx;
|
||||||
|
$uni-border-radius-base: 8rpx;
|
||||||
|
$uni-border-radius-lg: 12rpx;
|
||||||
|
$uni-border-radius-circle: 50%;
|
||||||
|
|
||||||
|
/* 水平间距 */
|
||||||
|
$uni-spacing-row-sm: 10rpx;
|
||||||
|
$uni-spacing-row-base: 20rpx;
|
||||||
|
$uni-spacing-row-lg: 30rpx;
|
||||||
|
|
||||||
|
/* 垂直间距 */
|
||||||
|
$uni-spacing-col-sm: 8rpx;
|
||||||
|
$uni-spacing-col-base: 16rpx;
|
||||||
|
$uni-spacing-col-lg: 24rpx;
|
||||||
|
|
||||||
|
/* 透明度 */
|
||||||
|
$uni-opacity-disabled: 0.3;
|
||||||
|
|
||||||
|
/* 文章场景相关 */
|
||||||
|
$uni-color-title: #2c405a;
|
||||||
|
$uni-color-subtitle: #555555;
|
||||||
|
$uni-color-paragraph: #3f536e;
|
||||||
257
src/utils/dataAdapter.js
Normal file
257
src/utils/dataAdapter.js
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
/**
|
||||||
|
* 数据源适配器(核心文件)
|
||||||
|
* 根据配置动态选择 Mock数据 或 真实API数据
|
||||||
|
*
|
||||||
|
* 这是保护Mock版本UI的核心机制:
|
||||||
|
* - Mock模式:使用本地Mock数据,不依赖后端,UI功能完整
|
||||||
|
* - API模式:调用真实后端接口,获取数据库数据
|
||||||
|
*
|
||||||
|
* 通过修改 config/env.config.js 中的 dataMode 即可切换模式
|
||||||
|
*/
|
||||||
|
|
||||||
|
import config from '@/config/env.config.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataAdapter 类
|
||||||
|
* 单例模式,全局统一管理数据源
|
||||||
|
*/
|
||||||
|
class DataAdapter {
|
||||||
|
constructor() {
|
||||||
|
this.mode = config.dataMode // 'mock' 或 'api'
|
||||||
|
this.debug = config.debug
|
||||||
|
this.mockDelay = config.mockDelay
|
||||||
|
|
||||||
|
// 延迟加载,避免循环依赖
|
||||||
|
this.mockData = null
|
||||||
|
this.apiService = null
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(`[DataAdapter] 初始化完成,当前模式: ${this.mode}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延迟加载 Mock 数据模块
|
||||||
|
*/
|
||||||
|
async _loadMockData() {
|
||||||
|
if (!this.mockData) {
|
||||||
|
const mockModule = await import('@/mock/index.js')
|
||||||
|
this.mockData = mockModule.default
|
||||||
|
}
|
||||||
|
return this.mockData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延迟加载 API 服务模块
|
||||||
|
*/
|
||||||
|
async _loadApiService() {
|
||||||
|
if (!this.apiService) {
|
||||||
|
const apiModule = await import('@/api/index.js')
|
||||||
|
this.apiService = apiModule.default
|
||||||
|
}
|
||||||
|
return this.apiService
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一数据获取接口
|
||||||
|
* @param {String} resource - 资源名称(如 'login', 'getMyAthletes')
|
||||||
|
* @param {Object} params - 请求参数
|
||||||
|
* @returns {Promise} 返回统一格式的响应
|
||||||
|
*/
|
||||||
|
async getData(resource, params = {}) {
|
||||||
|
if (this.mode === 'mock') {
|
||||||
|
return this._getMockData(resource, params)
|
||||||
|
} else {
|
||||||
|
return this._getApiData(resource, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Mock 数据
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _getMockData(resource, params) {
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(`[Mock数据] 请求: ${resource}`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 模拟网络延迟
|
||||||
|
if (this.mockDelay > 0) {
|
||||||
|
await this._delay(this.mockDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载Mock数据模块
|
||||||
|
const mockData = await this._loadMockData()
|
||||||
|
|
||||||
|
// 检查资源是否存在
|
||||||
|
if (!mockData[resource]) {
|
||||||
|
throw new Error(`Mock数据中未找到资源: ${resource}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用Mock数据函数
|
||||||
|
const data = mockData[resource](params)
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(`[Mock数据] 响应: ${resource}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回统一格式
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: '成功',
|
||||||
|
data,
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[Mock数据] 错误: ${resource}`, error)
|
||||||
|
throw {
|
||||||
|
code: 500,
|
||||||
|
message: error.message || 'Mock数据获取失败',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 API 数据
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _getApiData(resource, params) {
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(`[API请求] 请求: ${resource}`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 加载API服务模块
|
||||||
|
const apiService = await this._loadApiService()
|
||||||
|
|
||||||
|
// 检查接口是否存在
|
||||||
|
if (!apiService[resource]) {
|
||||||
|
throw new Error(`API服务中未找到接口: ${resource}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API接口
|
||||||
|
const response = await apiService[resource](params)
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(`[API请求] 响应: ${resource}`, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API响应已经是统一格式(由 request.js 处理)
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[API请求] 错误: ${resource}`, error)
|
||||||
|
// 重新抛出,由调用方处理
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延迟函数(模拟网络请求)
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_delay(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换数据模式
|
||||||
|
* @param {String} mode - 'mock' 或 'api'
|
||||||
|
*/
|
||||||
|
switchMode(mode) {
|
||||||
|
if (mode === 'mock' || mode === 'api') {
|
||||||
|
this.mode = mode
|
||||||
|
console.log(`[DataAdapter] 数据模式已切换为: ${mode}`)
|
||||||
|
} else {
|
||||||
|
console.error('[DataAdapter] 无效的数据模式,只能是 "mock" 或 "api"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前模式
|
||||||
|
* @returns {String} 'mock' 或 'api'
|
||||||
|
*/
|
||||||
|
getMode() {
|
||||||
|
return this.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为Mock模式
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isMockMode() {
|
||||||
|
return this.mode === 'mock'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为API模式
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isApiMode() {
|
||||||
|
return this.mode === 'api'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单例
|
||||||
|
export default new DataAdapter()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用示例:
|
||||||
|
*
|
||||||
|
* // 在页面中使用
|
||||||
|
* import dataAdapter from '@/utils/dataAdapter.js'
|
||||||
|
*
|
||||||
|
* export default {
|
||||||
|
* data() {
|
||||||
|
* return {
|
||||||
|
* players: []
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* async onLoad() {
|
||||||
|
* try {
|
||||||
|
* // 获取数据(自动根据配置选择Mock或API)
|
||||||
|
* const response = await dataAdapter.getData('getMyAthletes', {
|
||||||
|
* judgeId: 123,
|
||||||
|
* venueId: 1,
|
||||||
|
* projectId: 5
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* this.players = response.data
|
||||||
|
* } catch (error) {
|
||||||
|
* console.error('数据加载失败:', error.message)
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* methods: {
|
||||||
|
* // 查看当前模式
|
||||||
|
* checkMode() {
|
||||||
|
* console.log('当前数据模式:', dataAdapter.getMode())
|
||||||
|
* console.log('是否Mock模式:', dataAdapter.isMockMode())
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* // 动态切换模式(开发调试用)
|
||||||
|
* toggleMode() {
|
||||||
|
* const newMode = dataAdapter.isMockMode() ? 'api' : 'mock'
|
||||||
|
* dataAdapter.switchMode(newMode)
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* 资源名称(resource)与Mock/API的映射关系:
|
||||||
|
*
|
||||||
|
* | resource | Mock函数 | API函数 | 说明 |
|
||||||
|
* |---------------------|----------------------|---------------------|---------------|
|
||||||
|
* | login | mockData.login | apiService.login | 登录验证 |
|
||||||
|
* | getMyAthletes | mockData.getMyAthletes | apiService.getMyAthletes | 选手列表(评委) |
|
||||||
|
* | getAthletesForAdmin | mockData.getAthletesForAdmin | apiService.getAthletesForAdmin | 选手列表(裁判长) |
|
||||||
|
* | submitScore | mockData.submitScore | apiService.submitScore | 提交评分 |
|
||||||
|
* | getScoreDetail | mockData.getScoreDetail | apiService.getScoreDetail | 评分详情 |
|
||||||
|
* | modifyScore | mockData.modifyScore | apiService.modifyScore | 修改评分 |
|
||||||
|
* | getDeductions | mockData.getDeductions | apiService.getDeductions | 扣分项列表 |
|
||||||
|
* | getVenues | mockData.getVenues | apiService.getVenues | 场地列表 |
|
||||||
|
* | getProjects | mockData.getProjects | apiService.getProjects | 项目列表 |
|
||||||
|
*/
|
||||||
260
src/utils/request.js
Normal file
260
src/utils/request.js
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
/**
|
||||||
|
* 网络请求封装
|
||||||
|
* 统一处理HTTP请求、响应、错误、Token等
|
||||||
|
*
|
||||||
|
* 特性:
|
||||||
|
* - 自动添加Token(Blade-Auth格式)
|
||||||
|
* - 统一错误处理
|
||||||
|
* - 请求/响应拦截
|
||||||
|
* - 超时控制
|
||||||
|
* - Loading状态管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
import config from '@/config/env.config.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建请求头
|
||||||
|
* @param {Object} customHeader 自定义头部
|
||||||
|
* @returns {Object} 完整的请求头
|
||||||
|
*/
|
||||||
|
function getHeaders(customHeader = {}) {
|
||||||
|
const token = uni.getStorageSync('token') || ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
// 重要:后端使用 Blade-Auth 而不是 Authorization
|
||||||
|
'Blade-Auth': token ? `Bearer ${token}` : '',
|
||||||
|
...customHeader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一请求方法
|
||||||
|
* @param {Object} options 请求配置
|
||||||
|
* @param {String} options.url 请求路径(不含baseURL)
|
||||||
|
* @param {String} options.method 请求方法(GET/POST/PUT/DELETE)
|
||||||
|
* @param {Object} options.data 请求数据(POST/PUT使用)
|
||||||
|
* @param {Object} options.params 查询参数(GET使用)
|
||||||
|
* @param {Object} options.header 自定义请求头
|
||||||
|
* @param {Boolean} options.showLoading 是否显示Loading
|
||||||
|
* @param {String} options.loadingText Loading文本
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
function request(options = {}) {
|
||||||
|
const {
|
||||||
|
url = '',
|
||||||
|
method = 'GET',
|
||||||
|
data = {},
|
||||||
|
params = {},
|
||||||
|
header = {},
|
||||||
|
showLoading = false,
|
||||||
|
loadingText = '加载中...'
|
||||||
|
} = options
|
||||||
|
|
||||||
|
// 显示Loading
|
||||||
|
if (showLoading) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: loadingText,
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印调试信息
|
||||||
|
if (config.debug) {
|
||||||
|
console.log(`[API请求] ${method} ${url}`, method === 'GET' ? params : data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建完整URL(GET请求需要拼接查询参数)
|
||||||
|
let fullUrl = config.apiBaseURL + url
|
||||||
|
let requestData = data
|
||||||
|
|
||||||
|
// 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
|
||||||
|
requestData = undefined // GET请求不使用data字段
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: fullUrl,
|
||||||
|
method,
|
||||||
|
data: requestData,
|
||||||
|
header: getHeaders(header),
|
||||||
|
timeout: config.timeout,
|
||||||
|
success: (res) => {
|
||||||
|
if (config.debug) {
|
||||||
|
console.log(`[API响应] ${method} ${url}`, res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏Loading
|
||||||
|
if (showLoading) {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BladeX框架标准响应格式
|
||||||
|
// { code: 200, success: true, data: {}, msg: "操作成功" }
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
const response = res.data
|
||||||
|
|
||||||
|
// 业务成功
|
||||||
|
if (response.code === 200 || response.success) {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
message: response.msg || response.message || '成功',
|
||||||
|
data: response.data,
|
||||||
|
success: true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 业务失败
|
||||||
|
const errorMsg = response.msg || response.message || '请求失败'
|
||||||
|
uni.showToast({
|
||||||
|
title: errorMsg,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
reject({
|
||||||
|
code: response.code,
|
||||||
|
message: errorMsg,
|
||||||
|
data: response.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (res.statusCode === 401) {
|
||||||
|
// Token过期或未登录
|
||||||
|
uni.showToast({
|
||||||
|
title: 'Token已过期,请重新登录',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
// 清除Token
|
||||||
|
uni.removeStorageSync('token')
|
||||||
|
// 跳转到登录页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/login/login'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
reject({
|
||||||
|
code: 401,
|
||||||
|
message: 'Token已过期'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// HTTP错误
|
||||||
|
const errorMsg = `请求失败 (${res.statusCode})`
|
||||||
|
uni.showToast({
|
||||||
|
title: errorMsg,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
reject({
|
||||||
|
code: res.statusCode,
|
||||||
|
message: errorMsg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
if (config.debug) {
|
||||||
|
console.error(`[API错误] ${method} ${url}`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏Loading
|
||||||
|
if (showLoading) {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络错误
|
||||||
|
const errorMsg = err.errMsg || '网络错误,请检查网络连接'
|
||||||
|
uni.showToast({
|
||||||
|
title: errorMsg,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
reject({
|
||||||
|
code: -1,
|
||||||
|
message: errorMsg,
|
||||||
|
error: err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 请求
|
||||||
|
*/
|
||||||
|
export function get(url, params = {}, options = {}) {
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST 请求
|
||||||
|
*/
|
||||||
|
export function post(url, data = {}, options = {}) {
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT 请求
|
||||||
|
*/
|
||||||
|
export function put(url, data = {}, options = {}) {
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'PUT',
|
||||||
|
data,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE 请求
|
||||||
|
*/
|
||||||
|
export function del(url, data = {}, options = {}) {
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'DELETE',
|
||||||
|
data,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认导出
|
||||||
|
export default request
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用示例:
|
||||||
|
*
|
||||||
|
* // 方式1:直接使用 request
|
||||||
|
* import request from '@/utils/request.js'
|
||||||
|
*
|
||||||
|
* request({
|
||||||
|
* url: '/martial/score/list',
|
||||||
|
* method: 'GET',
|
||||||
|
* data: { page: 1, size: 10 },
|
||||||
|
* showLoading: true
|
||||||
|
* }).then(res => {
|
||||||
|
* console.log(res.data)
|
||||||
|
* }).catch(err => {
|
||||||
|
* console.error(err.message)
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // 方式2:使用快捷方法
|
||||||
|
* import { get, post, put, del } from '@/utils/request.js'
|
||||||
|
*
|
||||||
|
* // GET请求
|
||||||
|
* get('/martial/athlete/list', { venueId: 1 })
|
||||||
|
* .then(res => console.log(res.data))
|
||||||
|
*
|
||||||
|
* // POST请求
|
||||||
|
* post('/martial/score/submit', { athleteId: 1, score: 8.907 })
|
||||||
|
* .then(res => console.log(res.data))
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user