Files
martial-admin-mini/utils/dataAdapter.js
宅房 7ec9a77c2a 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行代码
- 完整的注释和使用说明
2025-12-11 14:06:03 +08:00

258 lines
6.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 数据源适配器(核心文件)
* 根据配置动态选择 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 | 项目列表 |
*/