You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
16 KiB
520 lines
16 KiB
/**
|
|
* UniApp Mock数据服务
|
|
* 支持环境变量控制,默认开启Mock数据
|
|
* 提供与API响应结构一致的数据格式
|
|
*/
|
|
|
|
import { isMockEnabled, isDebug } from '@/common/config.js'
|
|
|
|
// Mock数据规则 - 基于MockJS规则
|
|
const mockRules = {
|
|
// 用户数据规则
|
|
user: {
|
|
'id|+1': 1,
|
|
'username': '@cname',
|
|
'phone': /^1[3-9]\d{9}$/,
|
|
'avatar': '@image("100x100", "#50B347", "#FFF", "Avatar")',
|
|
'email': '@email',
|
|
'status|1': ['active', 'inactive'],
|
|
'created_at': '@datetime',
|
|
'updated_at': '@datetime'
|
|
},
|
|
|
|
// 学生数据规则
|
|
student: {
|
|
'id|+1': 1,
|
|
'name': '@cname',
|
|
'student_no': '@string("number", 10)',
|
|
'phone': /^1[3-9]\d{9}$/,
|
|
'avatar': '@image("100x100", "#50B347", "#FFF", "Student")',
|
|
'class_id|1-20': 1,
|
|
'status|1': ['active', 'inactive', 'graduated'],
|
|
'created_at': '@datetime'
|
|
},
|
|
|
|
// 课程数据规则
|
|
course: {
|
|
'id|+1': 1,
|
|
'name': '@ctitle(5, 15)',
|
|
'description': '@cparagraph(1, 3)',
|
|
'teacher_id|1-10': 1,
|
|
'price|100-1000.2': 1,
|
|
'duration|30-120': 1,
|
|
'status|1': ['active', 'inactive'],
|
|
'created_at': '@datetime'
|
|
},
|
|
|
|
// 课程表数据规则
|
|
schedule: {
|
|
'id|+1': 1,
|
|
'course_id|1-20': 1,
|
|
'teacher_id|1-10': 1,
|
|
'classroom': '@ctitle(3, 8)',
|
|
'date': '@date',
|
|
'start_time': '@time',
|
|
'end_time': '@time',
|
|
'status|1': ['scheduled', 'completed', 'cancelled']
|
|
}
|
|
}
|
|
|
|
// 生成Mock数据的函数
|
|
function generateMockData(rule, count = 1) {
|
|
const result = []
|
|
for (let i = 0; i < count; i++) {
|
|
const item = {}
|
|
for (const key in rule) {
|
|
if (key.includes('|')) {
|
|
const [field, mockRule] = key.split('|')
|
|
if (mockRule.includes('+')) {
|
|
item[field] = i + 1
|
|
} else if (mockRule.includes('-')) {
|
|
const [min, max] = mockRule.split('-')
|
|
item[field] = Math.floor(Math.random() * (max - min + 1)) + parseInt(min)
|
|
} else if (mockRule === '1') {
|
|
const options = rule[key]
|
|
item[field] = Array.isArray(options) ?
|
|
options[Math.floor(Math.random() * options.length)] :
|
|
options
|
|
}
|
|
} else {
|
|
item[key] = rule[key]
|
|
}
|
|
}
|
|
result.push(item)
|
|
}
|
|
return count === 1 ? result[0] : result
|
|
}
|
|
|
|
// Mock数据存储
|
|
const mockData = {
|
|
// 用户相关数据
|
|
users: generateMockData(mockRules.user, 50),
|
|
students: generateMockData(mockRules.student, 100),
|
|
courses: generateMockData(mockRules.course, 30),
|
|
schedules: generateMockData(mockRules.schedule, 200),
|
|
|
|
// 登录用户信息
|
|
currentUser: {
|
|
id: 1,
|
|
username: '张三',
|
|
phone: '13800138000',
|
|
avatar: 'https://via.placeholder.com/100x100?text=User',
|
|
email: 'zhangsan@example.com',
|
|
status: 'active',
|
|
role: 'student',
|
|
created_at: '2024-01-01 10:00:00',
|
|
updated_at: '2024-01-01 10:00:00'
|
|
},
|
|
|
|
// 学员信息 (xy_memberInfo)
|
|
memberInfo: {
|
|
id: 1001,
|
|
name: '李小明',
|
|
student_no: 'ST202401001',
|
|
phone: '13800138001',
|
|
age: 15,
|
|
gender: 1, // 1男 2女
|
|
classes_count: 8, // 我的课程数
|
|
sign_count: 15, // 已上课时
|
|
stay_sign_count: 5, // 待上课时
|
|
created_at: '2024-01-01 08:00:00',
|
|
memberHasOne: {
|
|
headimg: 'https://via.placeholder.com/144x144?text=Student',
|
|
id: 1001
|
|
},
|
|
customerResources: {
|
|
member: {
|
|
headimg: 'https://via.placeholder.com/144x144?text=Student'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 体测数据 (xy_physicalTest)
|
|
physicalTestData: {
|
|
data: [
|
|
{
|
|
id: 1,
|
|
resource_id: 1001,
|
|
height: 165,
|
|
weight: 55,
|
|
calculateChildHealthScore: 85,
|
|
created_at: '2024-01-10 14:30:00',
|
|
bmi: 20.2,
|
|
test_items: {
|
|
flexibility: 78,
|
|
strength: 85,
|
|
endurance: 82
|
|
}
|
|
}
|
|
],
|
|
total: 1,
|
|
page: 1,
|
|
pages: 1
|
|
},
|
|
|
|
// 课程安排数据 (xy_personCourseSchedule)
|
|
personCourseSchedule: {
|
|
data: [
|
|
{
|
|
id: 2001,
|
|
resources_id: 1001,
|
|
course_date: '2024-01-16',
|
|
time_slot: '09:00-10:30',
|
|
status: '0', // 0待上课 1已上课 2请假
|
|
courseScheduleHasOne: {
|
|
venue: {
|
|
venue_name: '篮球馆A'
|
|
},
|
|
course: {
|
|
course_name: '青少年篮球训练'
|
|
}
|
|
},
|
|
created_at: '2024-01-15 10:00:00'
|
|
},
|
|
{
|
|
id: 2002,
|
|
resources_id: 1001,
|
|
course_date: '2024-01-18',
|
|
time_slot: '14:00-15:30',
|
|
status: '0',
|
|
courseScheduleHasOne: {
|
|
venue: {
|
|
venue_name: '足球场B'
|
|
},
|
|
course: {
|
|
course_name: '足球基础课'
|
|
}
|
|
},
|
|
created_at: '2024-01-15 10:00:00'
|
|
}
|
|
],
|
|
total: 2,
|
|
page: 1,
|
|
pages: 1
|
|
},
|
|
|
|
// 作业数据 (xy_assignment)
|
|
assignmentData: {
|
|
data: [
|
|
{
|
|
id: 3001,
|
|
resources_id: 1001,
|
|
description: '完成本周的体能训练视频拍摄,展示标准动作',
|
|
content_type: 2, // 1图片 2视频 3文本
|
|
content_text: 'https://example.com/video.mp4',
|
|
status: '2', // 1待批改 2未提交 3已提交
|
|
created_at: '2024-01-14 16:00:00',
|
|
student: {
|
|
name: '李小明',
|
|
customerResources: {
|
|
member: {
|
|
headimg: 'https://via.placeholder.com/50x50?text=Student'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
id: 3002,
|
|
resources_id: 1001,
|
|
description: '上传训练心得体会,字数不少于200字',
|
|
content_type: 3,
|
|
content_text: '今天的训练很充实,学到了很多新的技巧...',
|
|
status: '3',
|
|
created_at: '2024-01-12 10:00:00',
|
|
student: {
|
|
name: '李小明',
|
|
customerResources: {
|
|
member: {
|
|
headimg: 'https://via.placeholder.com/50x50?text=Student'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
],
|
|
total: 2,
|
|
page: 1,
|
|
pages: 1
|
|
},
|
|
|
|
// 日历数据 (xy_personCourseScheduleGetCalendar)
|
|
calendarData: {
|
|
'2024-01-15': [
|
|
{
|
|
id: 2001,
|
|
course_name: '篮球训练',
|
|
time_slot: '09:00-10:30',
|
|
status: 'scheduled'
|
|
}
|
|
],
|
|
'2024-01-16': [
|
|
{
|
|
id: 2002,
|
|
course_name: '足球基础',
|
|
time_slot: '14:00-15:30',
|
|
status: 'scheduled'
|
|
}
|
|
]
|
|
},
|
|
|
|
// 教练信息 (xy_personCourseScheduleGetMyCoach)
|
|
coachData: {
|
|
data: [
|
|
{
|
|
id: 5001,
|
|
name: '王教练',
|
|
phone: '13800135001',
|
|
specialty: '篮球',
|
|
experience: '5年教学经验',
|
|
avatar: 'https://via.placeholder.com/100x100?text=Coach',
|
|
introduction: '专业篮球教练,擅长青少年基础训练和技能提升',
|
|
courses: ['青少年篮球', '篮球进阶训练']
|
|
}
|
|
],
|
|
total: 1
|
|
},
|
|
|
|
// 学生课程表
|
|
studentSchedules: [
|
|
{
|
|
id: 1,
|
|
course_name: '数学基础',
|
|
teacher_name: '王老师',
|
|
classroom: '101教室',
|
|
date: '2024-01-15',
|
|
start_time: '09:00',
|
|
end_time: '10:30',
|
|
status: 'scheduled'
|
|
},
|
|
{
|
|
id: 2,
|
|
course_name: '英语口语',
|
|
teacher_name: '李老师',
|
|
classroom: '202教室',
|
|
date: '2024-01-15',
|
|
start_time: '14:00',
|
|
end_time: '15:30',
|
|
status: 'scheduled'
|
|
}
|
|
],
|
|
|
|
// 考试成绩
|
|
examResults: [
|
|
{
|
|
id: 1,
|
|
exam_name: '期中考试',
|
|
course_name: '数学',
|
|
score: 85,
|
|
total_score: 100,
|
|
rank: 5,
|
|
exam_date: '2024-01-10',
|
|
status: 'published'
|
|
},
|
|
{
|
|
id: 2,
|
|
exam_name: '期末考试',
|
|
course_name: '英语',
|
|
score: 92,
|
|
total_score: 100,
|
|
rank: 2,
|
|
exam_date: '2024-01-12',
|
|
status: 'published'
|
|
}
|
|
]
|
|
}
|
|
|
|
// Mock服务类
|
|
class MockService {
|
|
constructor() {
|
|
this.enabled = isMockEnabled
|
|
this.debug = isDebug
|
|
this.init()
|
|
}
|
|
|
|
init() {
|
|
if (this.debug) {
|
|
console.log('Mock服务状态:', this.enabled ? '已启用' : '已禁用')
|
|
}
|
|
}
|
|
|
|
// 统一的响应格式
|
|
createResponse(data, code = 200, message = 'success') {
|
|
return {
|
|
code,
|
|
message,
|
|
data,
|
|
timestamp: Date.now()
|
|
}
|
|
}
|
|
|
|
// 分页响应格式
|
|
createPaginatedResponse(list, page = 1, size = 10, total = null) {
|
|
const actualTotal = total || list.length
|
|
const start = (page - 1) * size
|
|
const end = start + size
|
|
const paginatedList = list.slice(start, end)
|
|
|
|
return this.createResponse({
|
|
list: paginatedList,
|
|
total: actualTotal,
|
|
page: parseInt(page),
|
|
size: parseInt(size),
|
|
pages: Math.ceil(actualTotal / size)
|
|
})
|
|
}
|
|
|
|
// 模拟网络延迟
|
|
async delay(ms = 500) {
|
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
}
|
|
|
|
// 获取Mock数据
|
|
async getMockData(endpoint, params = {}) {
|
|
if (!this.enabled) {
|
|
return null
|
|
}
|
|
|
|
await this.delay()
|
|
|
|
// 根据端点返回相应的Mock数据 - 支持完整URL和方法名匹配
|
|
const checkEndpoint = (patterns) => {
|
|
return patterns.some(pattern => endpoint.includes(pattern))
|
|
}
|
|
|
|
// 学员信息
|
|
if (checkEndpoint(['/customerResourcesAuth/info', 'xy_memberInfo'])) {
|
|
return this.createResponse(mockData.memberInfo, 1, 'success')
|
|
}
|
|
|
|
// 体测数据
|
|
if (checkEndpoint(['/xy/physicalTest', 'xy_physicalTest'])) {
|
|
return this.createResponse(mockData.physicalTestData, 1, 'success')
|
|
}
|
|
|
|
// 课程安排
|
|
if (checkEndpoint(['/xy/personCourseSchedule', 'xy_personCourseSchedule']) && !endpoint.includes('getCalendar') && !endpoint.includes('getMyCoach')) {
|
|
// 根据status参数过滤数据
|
|
let scheduleData = mockData.personCourseSchedule.data
|
|
if (params.status !== undefined) {
|
|
scheduleData = scheduleData.filter(item => item.status === params.status)
|
|
}
|
|
return this.createResponse({
|
|
data: scheduleData,
|
|
total: scheduleData.length,
|
|
page: params.page || 1,
|
|
pages: Math.ceil(scheduleData.length / (params.limit || 10))
|
|
}, 1, 'success')
|
|
}
|
|
|
|
// 日历数据
|
|
if (checkEndpoint(['/xy/personCourseSchedule/getCalendar', 'xy_personCourseScheduleGetCalendar'])) {
|
|
return this.createResponse(mockData.calendarData, 1, 'success')
|
|
}
|
|
|
|
// 教练信息
|
|
if (checkEndpoint(['/xy/personCourseSchedule/getMyCoach', 'xy_personCourseScheduleGetMyCoach'])) {
|
|
return this.createResponse(mockData.coachData, 1, 'success')
|
|
}
|
|
|
|
// 作业列表
|
|
if (checkEndpoint(['/xy/assignment', 'xy_assignment']) && !endpoint.includes('/info') && !endpoint.includes('submitObj')) {
|
|
// 根据status参数过滤作业数据
|
|
let assignmentData = mockData.assignmentData.data
|
|
if (params.status !== undefined) {
|
|
assignmentData = assignmentData.filter(item => item.status === params.status)
|
|
}
|
|
return this.createResponse({
|
|
data: assignmentData,
|
|
total: assignmentData.length,
|
|
page: params.page || 1,
|
|
pages: Math.ceil(assignmentData.length / (params.limit || 10))
|
|
}, 1, 'success')
|
|
}
|
|
|
|
// 作业详情
|
|
if (checkEndpoint(['/xy/assignment/info', 'xy_assignmentsInfo'])) {
|
|
const assignmentId = params.id || params.assignment_id
|
|
const assignment = mockData.assignmentData.data.find(item => item.id == assignmentId)
|
|
return this.createResponse(assignment || {}, assignment ? 1 : 0, assignment ? 'success' : '作业不存在')
|
|
}
|
|
|
|
// 作业提交
|
|
if (checkEndpoint(['/xy/assignment/submitObj', 'xy_assignmentSubmitObj'])) {
|
|
return this.createResponse({}, 1, '作业提交成功')
|
|
}
|
|
|
|
// 学生登录
|
|
if (checkEndpoint(['/xy/login', 'xy_login'])) {
|
|
return this.createResponse({
|
|
token: 'mock_token_' + Date.now(),
|
|
user: mockData.memberInfo,
|
|
expires_in: 7200
|
|
}, 1, '登录成功')
|
|
}
|
|
|
|
// 原有的通用接口
|
|
switch (endpoint) {
|
|
case '/user/info':
|
|
return this.createResponse(mockData.currentUser)
|
|
|
|
case '/student/schedule':
|
|
return this.createResponse(mockData.studentSchedules)
|
|
|
|
case '/student/exam/results':
|
|
return this.createResponse(mockData.examResults)
|
|
|
|
case '/courses':
|
|
return this.createPaginatedResponse(
|
|
mockData.courses,
|
|
params.page || 1,
|
|
params.size || 10
|
|
)
|
|
|
|
case '/students':
|
|
return this.createPaginatedResponse(
|
|
mockData.students,
|
|
params.page || 1,
|
|
params.size || 10
|
|
)
|
|
|
|
default:
|
|
return this.createResponse(null, 404, '接口未找到')
|
|
}
|
|
}
|
|
|
|
// 检查是否应该使用Mock数据
|
|
shouldUseMock(url) {
|
|
if (!this.enabled) return false
|
|
|
|
// 定义需要Mock的接口列表
|
|
const mockableEndpoints = [
|
|
'/user/info',
|
|
'/student/schedule',
|
|
'/student/exam/results',
|
|
'/courses',
|
|
'/students',
|
|
// 学员端专用API - URL匹配
|
|
'/customerResourcesAuth/info', // xy_memberInfo
|
|
'/xy/physicalTest', // xy_physicalTest
|
|
'/xy/personCourseSchedule', // xy_personCourseSchedule相关
|
|
'/xy/assignment', // xy_assignment相关
|
|
'/xy/login', // xy_login
|
|
// 学员端专用API - 方法名匹配(用于开发调试)
|
|
'xy_memberInfo',
|
|
'xy_physicalTest',
|
|
'xy_personCourseSchedule',
|
|
'xy_assignment',
|
|
'xy_assignmentsInfo',
|
|
'xy_assignmentSubmitObj',
|
|
'xy_personCourseScheduleGetCalendar',
|
|
'xy_personCourseScheduleGetMyCoach',
|
|
'xy_login'
|
|
]
|
|
|
|
return mockableEndpoints.some(endpoint => url.includes(endpoint))
|
|
}
|
|
}
|
|
|
|
// 创建全局Mock服务实例
|
|
const mockService = new MockService()
|
|
|
|
export default mockService
|