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:
2025-12-11 14:06:03 +08:00
parent 7bd197f4ac
commit 7ec9a77c2a
12 changed files with 1616 additions and 1 deletions

245
utils/request.js Normal file
View File

@@ -0,0 +1,245 @@
/**
* 网络请求封装
* 统一处理HTTP请求、响应、错误、Token等
*
* 特性:
* - 自动添加TokenBlade-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))
*/