520 lines
15 KiB
JavaScript
520 lines
15 KiB
JavaScript
/**
|
||
* Drama Ling - 模擬數據
|
||
* 開發和測試用的模擬數據集
|
||
*/
|
||
|
||
/**
|
||
* 模擬用戶數據
|
||
*/
|
||
export const mockUsers = [
|
||
{
|
||
id: 'user_001',
|
||
email: 'demo@dramaling.com',
|
||
name: '張小明',
|
||
avatar: '/assets/media/images/avatar-1.png',
|
||
level: '中級',
|
||
joinDate: '2024-01-15',
|
||
preferences: {
|
||
language: 'zh-TW',
|
||
theme: 'auto',
|
||
soundEnabled: true,
|
||
notificationsEnabled: true
|
||
}
|
||
},
|
||
{
|
||
id: 'user_002',
|
||
email: 'sarah@example.com',
|
||
name: 'Sarah Chen',
|
||
avatar: '/assets/media/images/avatar-2.png',
|
||
level: '高級',
|
||
joinDate: '2023-11-20',
|
||
preferences: {
|
||
language: 'zh-TW',
|
||
theme: 'light',
|
||
soundEnabled: false,
|
||
notificationsEnabled: true
|
||
}
|
||
}
|
||
];
|
||
|
||
/**
|
||
* 模擬用戶統計數據
|
||
*/
|
||
export const mockUserStats = {
|
||
totalWords: 1247,
|
||
studyTime: 45, // 小時
|
||
achievements: 12,
|
||
streak: 7, // 連續學習天數
|
||
diamonds: 2580,
|
||
weeklyGoal: 5, // 每週學習時數目標
|
||
weeklyProgress: 3.5, // 本週已完成時數
|
||
levelProgress: 78, // 等級進度百分比
|
||
accuracy: 85, // 整體答題準確率
|
||
strongCategories: ['商務', '旅遊', '日常對話'],
|
||
weakCategories: ['科技', '醫療']
|
||
};
|
||
|
||
/**
|
||
* 模擬詞彙數據
|
||
*/
|
||
export const mockVocabulary = [
|
||
{
|
||
id: 'vocab_001',
|
||
word: 'appreciate',
|
||
definition: '感激;欣賞;理解',
|
||
pronunciation: '/əˈpriːʃieɪt/',
|
||
difficulty: 'intermediate',
|
||
category: 'business',
|
||
examples: [
|
||
{
|
||
english: 'I appreciate your hard work.',
|
||
chinese: '我很感激你的辛勤工作。'
|
||
},
|
||
{
|
||
english: 'Do you appreciate classical music?',
|
||
chinese: '你欣賞古典音樂嗎?'
|
||
}
|
||
],
|
||
audioUrl: '/assets/media/audio/appreciate.mp3',
|
||
imageUrl: '/assets/media/images/appreciate.jpg',
|
||
tags: ['emotion', 'business', 'common'],
|
||
addedDate: '2024-09-01',
|
||
masteryLevel: 0.8, // 0-1,掌握程度
|
||
reviewCount: 5,
|
||
correctCount: 4,
|
||
lastReviewed: '2024-09-09'
|
||
},
|
||
{
|
||
id: 'vocab_002',
|
||
word: 'definitely',
|
||
definition: '當然;肯定地',
|
||
pronunciation: '/ˈdefɪnətli/',
|
||
difficulty: 'beginner',
|
||
category: 'daily',
|
||
examples: [
|
||
{
|
||
english: 'I will definitely come to your party.',
|
||
chinese: '我一定會參加你的派對。'
|
||
},
|
||
{
|
||
english: 'That was definitely the best movie I\'ve seen.',
|
||
chinese: '那絕對是我看過最好的電影。'
|
||
}
|
||
],
|
||
audioUrl: '/assets/media/audio/definitely.mp3',
|
||
imageUrl: '/assets/media/images/definitely.jpg',
|
||
tags: ['adverb', 'confirmation', 'common'],
|
||
addedDate: '2024-08-28',
|
||
masteryLevel: 0.95,
|
||
reviewCount: 8,
|
||
correctCount: 8,
|
||
lastReviewed: '2024-09-08'
|
||
},
|
||
{
|
||
id: 'vocab_003',
|
||
word: 'entrepreneur',
|
||
definition: '企業家;創業者',
|
||
pronunciation: '/ˌɒntrəprəˈnɜː(r)/',
|
||
difficulty: 'advanced',
|
||
category: 'business',
|
||
examples: [
|
||
{
|
||
english: 'She is a successful entrepreneur.',
|
||
chinese: '她是一位成功的企業家。'
|
||
},
|
||
{
|
||
english: 'Many entrepreneurs start with small businesses.',
|
||
chinese: '許多企業家都是從小企業開始的。'
|
||
}
|
||
],
|
||
audioUrl: '/assets/media/audio/entrepreneur.mp3',
|
||
imageUrl: '/assets/media/images/entrepreneur.jpg',
|
||
tags: ['business', 'career', 'advanced'],
|
||
addedDate: '2024-09-05',
|
||
masteryLevel: 0.4,
|
||
reviewCount: 2,
|
||
correctCount: 1,
|
||
lastReviewed: '2024-09-07'
|
||
}
|
||
];
|
||
|
||
/**
|
||
* 模擬對話場景數據
|
||
*/
|
||
export const mockDialogues = [
|
||
{
|
||
id: 'dialogue_001',
|
||
title: '餐廳點餐',
|
||
category: 'restaurant',
|
||
difficulty: 'beginner',
|
||
description: '在餐廳與服務生的基本對話練習',
|
||
estimatedTime: 5, // 分鐘
|
||
thumbnail: '/assets/media/images/restaurant-scene.jpg',
|
||
characters: [
|
||
{
|
||
id: 'customer',
|
||
name: '顧客',
|
||
avatar: '/assets/media/images/customer-avatar.png'
|
||
},
|
||
{
|
||
id: 'waiter',
|
||
name: '服務生',
|
||
avatar: '/assets/media/images/waiter-avatar.png',
|
||
isAI: true
|
||
}
|
||
],
|
||
conversation: [
|
||
{
|
||
speaker: 'waiter',
|
||
text: 'Good evening! Welcome to our restaurant. How many people?',
|
||
chinese: '晚安!歡迎來到我們餐廳。請問幾位?',
|
||
audioUrl: '/assets/media/audio/dialogue_001_01.mp3'
|
||
},
|
||
{
|
||
speaker: 'customer',
|
||
text: 'Table for two, please.',
|
||
chinese: '請給我們兩人桌。',
|
||
audioUrl: '/assets/media/audio/dialogue_001_02.mp3',
|
||
isUserResponse: true,
|
||
alternatives: [
|
||
'Two people, please.',
|
||
'A table for two.',
|
||
'Party of two.'
|
||
]
|
||
},
|
||
{
|
||
speaker: 'waiter',
|
||
text: 'Perfect! Right this way, please. Here are your menus.',
|
||
chinese: '很好!請這邊走。這是菜單。',
|
||
audioUrl: '/assets/media/audio/dialogue_001_03.mp3'
|
||
}
|
||
],
|
||
completedBy: 1250, // 完成人數
|
||
rating: 4.8,
|
||
tags: ['restaurant', 'basic', 'ordering']
|
||
},
|
||
{
|
||
id: 'dialogue_002',
|
||
title: '機場報到',
|
||
category: 'travel',
|
||
difficulty: 'intermediate',
|
||
description: '在機場辦理登機手續的對話',
|
||
estimatedTime: 8,
|
||
thumbnail: '/assets/media/images/airport-scene.jpg',
|
||
characters: [
|
||
{
|
||
id: 'passenger',
|
||
name: '乘客',
|
||
avatar: '/assets/media/images/passenger-avatar.png'
|
||
},
|
||
{
|
||
id: 'checkin_agent',
|
||
name: '報到人員',
|
||
avatar: '/assets/media/images/agent-avatar.png',
|
||
isAI: true
|
||
}
|
||
],
|
||
conversation: [
|
||
{
|
||
speaker: 'checkin_agent',
|
||
text: 'Good morning! May I have your passport and ticket, please?',
|
||
chinese: '早安!請出示您的護照和機票。',
|
||
audioUrl: '/assets/media/audio/dialogue_002_01.mp3'
|
||
},
|
||
{
|
||
speaker: 'passenger',
|
||
text: 'Here you go.',
|
||
chinese: '給您。',
|
||
audioUrl: '/assets/media/audio/dialogue_002_02.mp3',
|
||
isUserResponse: true,
|
||
alternatives: [
|
||
'Here they are.',
|
||
'Sure, here you are.',
|
||
'Of course.'
|
||
]
|
||
}
|
||
],
|
||
completedBy: 890,
|
||
rating: 4.6,
|
||
tags: ['travel', 'airport', 'procedures']
|
||
}
|
||
];
|
||
|
||
/**
|
||
* 模擬學習進度數據
|
||
*/
|
||
export const mockLearningProgress = {
|
||
dailyStats: [
|
||
{ date: '2024-09-03', wordsLearned: 15, timeSpent: 25, accuracy: 82 },
|
||
{ date: '2024-09-04', wordsLearned: 12, timeSpent: 30, accuracy: 88 },
|
||
{ date: '2024-09-05', wordsLearned: 18, timeSpent: 35, accuracy: 75 },
|
||
{ date: '2024-09-06', wordsLearned: 20, timeSpent: 40, accuracy: 90 },
|
||
{ date: '2024-09-07', wordsLearned: 16, timeSpent: 28, accuracy: 85 },
|
||
{ date: '2024-09-08', wordsLearned: 14, timeSpent: 32, accuracy: 87 },
|
||
{ date: '2024-09-09', wordsLearned: 22, timeSpent: 45, accuracy: 92 }
|
||
],
|
||
weeklyGoals: {
|
||
wordsTarget: 100,
|
||
timeTarget: 5 * 60, // 5小時,以分鐘為單位
|
||
currentWords: 117,
|
||
currentTime: 235 // 分鐘
|
||
},
|
||
categoryProgress: {
|
||
business: { total: 150, mastered: 120, learning: 25, difficult: 5 },
|
||
daily: { total: 200, mastered: 180, learning: 15, difficult: 5 },
|
||
travel: { total: 80, mastered: 60, learning: 15, difficult: 5 },
|
||
technology: { total: 100, mastered: 45, learning: 30, difficult: 25 }
|
||
},
|
||
achievements: [
|
||
{
|
||
id: 'first_week',
|
||
title: '初次體驗',
|
||
description: '完成第一週學習',
|
||
icon: '🎯',
|
||
unlockedDate: '2024-01-22',
|
||
rarity: 'common'
|
||
},
|
||
{
|
||
id: 'streak_7',
|
||
title: '七日連勝',
|
||
description: '連續學習7天',
|
||
icon: '🔥',
|
||
unlockedDate: '2024-02-01',
|
||
rarity: 'rare'
|
||
},
|
||
{
|
||
id: 'words_1000',
|
||
title: '詞彙大師',
|
||
description: '學習1000個詞彙',
|
||
icon: '📚',
|
||
unlockedDate: '2024-08-15',
|
||
rarity: 'epic'
|
||
}
|
||
]
|
||
};
|
||
|
||
/**
|
||
* 模擬系統設定數據
|
||
*/
|
||
export const mockSystemSettings = {
|
||
themes: [
|
||
{ id: 'auto', name: '自動', description: '跟隨系統設定' },
|
||
{ id: 'light', name: '淺色', description: '淺色主題' },
|
||
{ id: 'dark', name: '深色', description: '深色主題' }
|
||
],
|
||
languages: [
|
||
{ id: 'zh-TW', name: '繁體中文', flag: '🇹🇼' },
|
||
{ id: 'zh-CN', name: '简体中文', flag: '🇨🇳' },
|
||
{ id: 'en-US', name: 'English', flag: '🇺🇸' },
|
||
{ id: 'ja-JP', name: '日本語', flag: '🇯🇵' }
|
||
],
|
||
notificationTypes: [
|
||
{ id: 'daily_reminder', name: '每日提醒', description: '提醒您每天學習' },
|
||
{ id: 'streak_warning', name: '連勝警告', description: '連勝即將中斷時提醒' },
|
||
{ id: 'achievement', name: '成就通知', description: '獲得新成就時通知' },
|
||
{ id: 'weekly_report', name: '週報', description: '每週學習報告' }
|
||
]
|
||
};
|
||
|
||
/**
|
||
* 模擬 API 響應數據
|
||
*/
|
||
export const mockApiResponses = {
|
||
login: {
|
||
success: {
|
||
token: 'mock_jwt_token_12345',
|
||
user: mockUsers[0],
|
||
expiresIn: 86400 // 24小時
|
||
},
|
||
error: {
|
||
message: '帳號或密碼錯誤',
|
||
code: 'INVALID_CREDENTIALS'
|
||
}
|
||
},
|
||
|
||
getUserStats: {
|
||
success: mockUserStats,
|
||
error: {
|
||
message: '無法載入用戶統計',
|
||
code: 'STATS_LOAD_ERROR'
|
||
}
|
||
},
|
||
|
||
getVocabulary: {
|
||
success: {
|
||
data: mockVocabulary,
|
||
total: mockVocabulary.length,
|
||
page: 1,
|
||
limit: 20
|
||
},
|
||
error: {
|
||
message: '無法載入詞彙數據',
|
||
code: 'VOCABULARY_LOAD_ERROR'
|
||
}
|
||
},
|
||
|
||
getDialogues: {
|
||
success: {
|
||
data: mockDialogues,
|
||
total: mockDialogues.length,
|
||
page: 1,
|
||
limit: 10
|
||
},
|
||
error: {
|
||
message: '無法載入對話數據',
|
||
code: 'DIALOGUE_LOAD_ERROR'
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 模擬 API 客戶端
|
||
* 用於開發環境的 API 模擬
|
||
*/
|
||
export class MockApiClient {
|
||
constructor() {
|
||
this.delay = 300; // 模擬網路延遲
|
||
this.failureRate = 0.1; // 10% 機率失敗
|
||
}
|
||
|
||
/**
|
||
* 模擬 API 延遲
|
||
*/
|
||
async simulateDelay() {
|
||
return new Promise(resolve => {
|
||
setTimeout(resolve, this.delay);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 模擬隨機失敗
|
||
*/
|
||
simulateRandomFailure() {
|
||
return Math.random() < this.failureRate;
|
||
}
|
||
|
||
/**
|
||
* 用戶登入
|
||
*/
|
||
async login(email, password) {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error(mockApiResponses.login.error.message);
|
||
}
|
||
|
||
// 簡單的驗證邏輯
|
||
if (email === 'demo@dramaling.com' && password === 'password') {
|
||
return mockApiResponses.login.success;
|
||
} else {
|
||
throw new Error(mockApiResponses.login.error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 獲取用戶統計
|
||
*/
|
||
async getUserStats() {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error(mockApiResponses.getUserStats.error.message);
|
||
}
|
||
|
||
return mockApiResponses.getUserStats.success;
|
||
}
|
||
|
||
/**
|
||
* 獲取詞彙列表
|
||
*/
|
||
async getVocabulary(params = {}) {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error(mockApiResponses.getVocabulary.error.message);
|
||
}
|
||
|
||
let filteredVocabulary = [...mockVocabulary];
|
||
|
||
// 模擬過濾和排序
|
||
if (params.category) {
|
||
filteredVocabulary = filteredVocabulary.filter(
|
||
vocab => vocab.category === params.category
|
||
);
|
||
}
|
||
|
||
if (params.difficulty) {
|
||
filteredVocabulary = filteredVocabulary.filter(
|
||
vocab => vocab.difficulty === params.difficulty
|
||
);
|
||
}
|
||
|
||
return {
|
||
data: filteredVocabulary,
|
||
total: filteredVocabulary.length,
|
||
page: params.page || 1,
|
||
limit: params.limit || 20
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 獲取對話場景
|
||
*/
|
||
async getDialogues(params = {}) {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error(mockApiResponses.getDialogues.error.message);
|
||
}
|
||
|
||
let filteredDialogues = [...mockDialogues];
|
||
|
||
if (params.category) {
|
||
filteredDialogues = filteredDialogues.filter(
|
||
dialogue => dialogue.category === params.category
|
||
);
|
||
}
|
||
|
||
if (params.difficulty) {
|
||
filteredDialogues = filteredDialogues.filter(
|
||
dialogue => dialogue.difficulty === params.difficulty
|
||
);
|
||
}
|
||
|
||
return {
|
||
data: filteredDialogues,
|
||
total: filteredDialogues.length,
|
||
page: params.page || 1,
|
||
limit: params.limit || 10
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 獲取學習進度
|
||
*/
|
||
async getLearningProgress() {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error('無法載入學習進度');
|
||
}
|
||
|
||
return mockLearningProgress;
|
||
}
|
||
|
||
/**
|
||
* 獲取當前用戶
|
||
*/
|
||
async getCurrentUser() {
|
||
await this.simulateDelay();
|
||
|
||
if (this.simulateRandomFailure()) {
|
||
throw new Error('無法載入用戶資料');
|
||
}
|
||
|
||
return mockUsers[0];
|
||
}
|
||
}
|
||
|
||
// 導出模擬 API 客戶端實例
|
||
export const mockApi = new MockApiClient(); |