forked from wangziqi/gongxue-base
feat: upgrade student vocabulary learning flow
This commit is contained in:
@@ -161,6 +161,8 @@ export interface VocabularyWord {
|
||||
difficulty?: string | null;
|
||||
status?: string;
|
||||
dueLevel?: string;
|
||||
favoritedAt?: string | null;
|
||||
note?: string | null;
|
||||
}
|
||||
|
||||
export async function createPracticeSession(body: {
|
||||
@@ -281,6 +283,12 @@ export async function toggleWordFavorite(wordId: string, favorite: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadFavoriteWords(unitId?: string, limit = 500) {
|
||||
return apiRequest<{ items?: VocabularyWord[] }>('/api/learning/vocabulary/favorites', {
|
||||
query: { unitId, limit },
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadVocabularyStats(unitId?: string) {
|
||||
return apiRequest<{ item?: Record<string, unknown> }>('/api/learning/vocabulary/stats', { query: { unitId } });
|
||||
}
|
||||
|
||||
97
apps/taro/src/services/pronunciation.ts
Normal file
97
apps/taro/src/services/pronunciation.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
|
||||
export type AccentType = 'us' | 'uk';
|
||||
|
||||
const YOUDAO_TYPE: Record<AccentType, number> = {
|
||||
us: 1,
|
||||
uk: 2,
|
||||
};
|
||||
|
||||
let currentAudio: HTMLAudioElement | null = null;
|
||||
let currentInnerAudio: Taro.InnerAudioContext | null = null;
|
||||
|
||||
function pronunciationUrl(word: string, accent: AccentType) {
|
||||
return `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(word)}&type=${YOUDAO_TYPE[accent] || 1}`;
|
||||
}
|
||||
|
||||
function stopCurrentAudio() {
|
||||
try {
|
||||
if (currentAudio) {
|
||||
currentAudio.pause();
|
||||
currentAudio.src = '';
|
||||
currentAudio = null;
|
||||
}
|
||||
} catch (_error) {
|
||||
// ignore playback cleanup errors
|
||||
}
|
||||
try {
|
||||
if (currentInnerAudio) {
|
||||
currentInnerAudio.stop();
|
||||
currentInnerAudio.destroy();
|
||||
currentInnerAudio = null;
|
||||
}
|
||||
} catch (_error) {
|
||||
// ignore playback cleanup errors
|
||||
}
|
||||
try {
|
||||
if (typeof window !== 'undefined' && 'speechSynthesis' in window) {
|
||||
window.speechSynthesis.cancel();
|
||||
}
|
||||
} catch (_error) {
|
||||
// ignore unsupported runtime
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackSpeech(word: string, accent: AccentType) {
|
||||
if (typeof window === 'undefined' || !('speechSynthesis' in window)) return false;
|
||||
try {
|
||||
const utterance = new SpeechSynthesisUtterance(word);
|
||||
utterance.lang = accent === 'uk' ? 'en-GB' : 'en-US';
|
||||
utterance.rate = 0.86;
|
||||
window.speechSynthesis.speak(utterance);
|
||||
return true;
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function playWordPronunciation(word: string, accent: AccentType = 'us') {
|
||||
const text = word.trim();
|
||||
if (!text) return;
|
||||
stopCurrentAudio();
|
||||
|
||||
const url = pronunciationUrl(text, accent);
|
||||
|
||||
if (process.env.TARO_ENV === 'h5' && typeof Audio !== 'undefined') {
|
||||
try {
|
||||
const audio = new Audio(url);
|
||||
audio.preload = 'auto';
|
||||
currentAudio = audio;
|
||||
audio.onerror = () => {
|
||||
currentAudio = null;
|
||||
fallbackSpeech(text, accent);
|
||||
};
|
||||
await audio.play();
|
||||
return;
|
||||
} catch (_error) {
|
||||
if (fallbackSpeech(text, accent)) return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const audio = Taro.createInnerAudioContext();
|
||||
currentInnerAudio = audio;
|
||||
audio.src = url;
|
||||
audio.autoplay = true;
|
||||
audio.onEnded(() => {
|
||||
if (currentInnerAudio === audio) currentInnerAudio = null;
|
||||
audio.destroy();
|
||||
});
|
||||
audio.onError(() => {
|
||||
if (currentInnerAudio === audio) currentInnerAudio = null;
|
||||
audio.destroy();
|
||||
});
|
||||
} catch (_error) {
|
||||
Taro.showToast({ title: '当前环境暂不支持发音', icon: 'none' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user