feat: 添加Mock版本保护机制 - 基础架构完成
完成内容: ✅ 第一层保护: Git分支隔离 - 创建 v1.0-mock 标签 - 创建 feature/api-integration 分支 ✅ 第二层保护: 配置开关控制 - config/env.config.js (环境配置,支持Mock/API模式切换) ✅ 第三层保护: 代码架构分离 - utils/request.js (网络请求封装,支持Blade-Auth) - utils/dataAdapter.js (核心适配器,自动选择数据源) ✅ Mock数据模块 (4个文件): - mock/index.js (统一入口) - mock/login.js (登录Mock数据) - mock/athlete.js (选手Mock数据,含场地、项目) - mock/score.js (评分Mock数据,含扣分项、详情、修改) ✅ API接口模块 (4个文件): - api/index.js (统一入口) - api/auth.js (认证API,含后端接口规范) - api/athlete.js (选手API,含SQL示例) - api/score.js (评分API,含实现逻辑说明) 特性: - 通过修改 config/env.config.js 的 dataMode 即可切换Mock/API模式 - Mock模式: 完全离线,无需后端,UI功能完整 - API模式: 调用真实后端接口(需后端实现5个专用接口) - 零UI修改: 原有页面代码完全保护,仅替换数据源 下一步: - 修改5个页面使用 dataAdapter - 测试Mock模式功能 - 后端开发5个小程序专用接口 代码统计: - 新增11个文件 - 约1000行代码 - 完整的注释和使用说明
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
"allow": [
|
||||
"Bash(tree:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git tag:*)",
|
||||
"Bash(git checkout:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
142
api/athlete.js
Normal file
142
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',
|
||||
data: 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',
|
||||
data: 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',
|
||||
data: {
|
||||
...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',
|
||||
data: {
|
||||
...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
api/auth.js
Normal file
85
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: '/api/mini/login',
|
||||
method: 'POST',
|
||||
data,
|
||||
showLoading: true,
|
||||
loadingText: '登录中...'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/api/mini/logout',
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Token验证
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function verifyToken() {
|
||||
return request({
|
||||
url: '/api/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
api/index.js
Normal file
158
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
api/score.js
Normal file
165
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',
|
||||
data: {
|
||||
...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
|
||||
*/
|
||||
75
config/env.config.js
Normal file
75
config/env.config.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 环境配置文件
|
||||
* 控制应用的数据源模式(Mock数据 或 真实API)
|
||||
*
|
||||
* 使用说明:
|
||||
* 1. Mock模式(UI演示、前端独立开发):设置 dataMode: 'mock'
|
||||
* 2. API模式(真实数据对接):设置 dataMode: 'api'
|
||||
* 3. 可在代码中动态切换模式
|
||||
*/
|
||||
|
||||
const ENV_CONFIG = {
|
||||
// 开发环境配置
|
||||
development: {
|
||||
// 数据模式: 'mock' | 'api'
|
||||
// mock - 使用本地Mock数据(保护UI版本)
|
||||
// api - 调用真实后端接口
|
||||
dataMode: 'mock',
|
||||
|
||||
// API基础路径(dataMode为'api'时使用)
|
||||
apiBaseURL: 'http://localhost:8080',
|
||||
|
||||
// 是否开启调试模式
|
||||
debug: true,
|
||||
|
||||
// 请求超时时间(毫秒)
|
||||
timeout: 30000,
|
||||
|
||||
// 是否模拟网络延迟(仅Mock模式)
|
||||
mockDelay: 300
|
||||
},
|
||||
|
||||
// 测试环境配置
|
||||
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)
|
||||
*/
|
||||
162
mock/athlete.js
Normal file
162
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} 项目列表
|
||||
*/
|
||||
export function getProjects(params) {
|
||||
return [
|
||||
'女子组长拳',
|
||||
'男子组陈氏太极拳',
|
||||
'女子组双剑(含长穗双剑)',
|
||||
'男子组杨氏太极拳',
|
||||
'女子组刀术',
|
||||
'男子组棍术',
|
||||
'女子组枪术',
|
||||
'男子组剑术'
|
||||
]
|
||||
}
|
||||
|
||||
export default {
|
||||
getMyAthletes,
|
||||
getAthletesForAdmin,
|
||||
getVenues,
|
||||
getProjects
|
||||
}
|
||||
117
mock/index.js
Normal file
117
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
mock/login.js
Normal file
44
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
mock/score.js
Normal file
162
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
|
||||
}
|
||||
257
utils/dataAdapter.js
Normal file
257
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 | 项目列表 |
|
||||
*/
|
||||
245
utils/request.js
Normal file
245
utils/request.js
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* 网络请求封装
|
||||
* 统一处理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 请求数据
|
||||
* @param {Object} options.header 自定义请求头
|
||||
* @param {Boolean} options.showLoading 是否显示Loading
|
||||
* @param {String} options.loadingText Loading文本
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function request(options = {}) {
|
||||
const {
|
||||
url = '',
|
||||
method = 'GET',
|
||||
data = {},
|
||||
header = {},
|
||||
showLoading = false,
|
||||
loadingText = '加载中...'
|
||||
} = options
|
||||
|
||||
// 显示Loading
|
||||
if (showLoading) {
|
||||
uni.showLoading({
|
||||
title: loadingText,
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
// 打印调试信息
|
||||
if (config.debug) {
|
||||
console.log(`[API请求] ${method} ${url}`, data)
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: config.apiBaseURL + url,
|
||||
method,
|
||||
data,
|
||||
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, data = {}, options = {}) {
|
||||
return request({
|
||||
url,
|
||||
method: 'GET',
|
||||
data,
|
||||
...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