|
|
@@ -205,6 +205,10 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
readonly current = computed(() => this.words()[this.currentIndex()] ?? null);
|
|
|
readonly completedSelectionWords = computed(() => this.words().filter((word) => this.selectionWordCompleted(word)));
|
|
|
readonly pendingSelectionWords = computed(() => this.words().filter((word) => !this.selectionWordCompleted(word)));
|
|
|
+ readonly learnedNewWords = computed(() => this.words().filter((word) => this.newWordLearned(word)));
|
|
|
+ readonly unlearnedNewWords = computed(() => this.words().filter((word) => !this.newWordLearned(word)));
|
|
|
+ readonly visibleNewWords = computed(() => this.activeSelectionBucket() === 'completed' ? this.learnedNewWords() : this.unlearnedNewWords());
|
|
|
+ readonly allVisibleNewWordsSelected = computed(() => this.visibleNewWords().length > 0 && this.visibleNewWords().every((word) => this.selected().has(word.id)));
|
|
|
readonly newlyKnownWords = computed(() => this.words().filter((word) => !this.wordHistoricallyLearned(word) && this.selectionAssessment()[word.id] === 'known'));
|
|
|
readonly selectionActionLabel = computed(() => {
|
|
|
const hasLearningWords = this.selected().size > 0;
|
|
|
@@ -319,6 +323,10 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
return ['pages/stu/stu_abc', 'pages/his/his_words', 'pages/member/starshop'].includes(this.meta.path);
|
|
|
}
|
|
|
|
|
|
+ get isNewWordSelectionPage(): boolean {
|
|
|
+ return this.meta.path === 'pages/member/starshop';
|
|
|
+ }
|
|
|
+
|
|
|
get isHistoryWordView(): boolean {
|
|
|
return this.meta.path === 'pages/his/his_words';
|
|
|
}
|
|
|
@@ -364,6 +372,10 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
return ['pages/stu/stu5', 'pages/stu/pl_stu5'].includes(this.meta.path);
|
|
|
}
|
|
|
|
|
|
+ get spellingStageSkipped(): boolean {
|
|
|
+ return this.storageGet('skip_spelling_stage') === true;
|
|
|
+ }
|
|
|
+
|
|
|
get canSkipSpellingStage(): boolean {
|
|
|
return this.meta.path === 'pages/stu/pl_stu5'
|
|
|
|| (this.meta.path === 'pages/stu/stu5' && this.isNewWordSelfStudyStage);
|
|
|
@@ -378,7 +390,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
get stageActionLabel(): string {
|
|
|
if (this.isMatchingPage) return '进入听音识意';
|
|
|
- if (this.isListeningPage && this.storageGet('skip_spelling_stage') !== true) return '进入组合拼写';
|
|
|
+ if (this.isListeningPage && !this.spellingStageSkipped) return '进入组合拼写';
|
|
|
if (this.hasNextWordBatch) return '学习下一组单词';
|
|
|
const mode = String(this.storageGet('learn_mode') ?? '');
|
|
|
if (mode === 'newword' || mode === 'history_word') return '完成学习并返回';
|
|
|
@@ -570,6 +582,10 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
return Number(word['w_learned'] ?? 0) > 0;
|
|
|
}
|
|
|
|
|
|
+ newWordLearned(word: Word): boolean {
|
|
|
+ return Number(word['xxcs'] ?? 0) > 0 || this.wordHistoricallyLearned(word);
|
|
|
+ }
|
|
|
+
|
|
|
selectionWordCompleted(word: Word): boolean {
|
|
|
return this.wordHistoricallyLearned(word) || this.selectionAssessment()[word.id] === 'known';
|
|
|
}
|
|
|
@@ -598,6 +614,17 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
|
|
|
|
toggleAllWords(): void {
|
|
|
+ if (this.isNewWordSelectionPage) {
|
|
|
+ const visible = this.visibleNewWords();
|
|
|
+ const clearVisible = this.allVisibleNewWordsSelected();
|
|
|
+ const next = new Set(this.selected());
|
|
|
+ for (const word of visible) {
|
|
|
+ if (clearVisible) next.delete(word.id);
|
|
|
+ else next.add(word.id);
|
|
|
+ }
|
|
|
+ this.selected.set(next);
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (this.selected().size) {
|
|
|
this.selected.set(new Set());
|
|
|
return;
|
|
|
@@ -801,7 +828,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
this.navigateStage('/pages/stu/stu4');
|
|
|
} else if (this.isListeningPage) {
|
|
|
this.completed.set(false);
|
|
|
- if (this.storageGet('skip_spelling_stage') === true) this.finishSpellingFlow();
|
|
|
+ if (this.spellingStageSkipped) this.finishSpellingFlow();
|
|
|
else this.navigateStage(this.canStartPointReading ? '/pages/stu/pl_stu5' : '/pages/stu/stu5');
|
|
|
} else if (this.isSpellingPage) {
|
|
|
this.finishSpellingFlow();
|
|
|
@@ -1298,18 +1325,36 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
private finishSpellingFlow(): void {
|
|
|
const currentPage = Number(this.storageGet('_page_current_learn') ?? 1);
|
|
|
+ const mode = String(this.storageGet('learn_mode') ?? '');
|
|
|
+ if (mode === 'newword') {
|
|
|
+ const learner = this.canStartPointReading ? this.session.student() : this.session.user();
|
|
|
+ this.submitting.set(true);
|
|
|
+ this.persistWordsAsLearned(this.words(), learner).pipe(finalize(() => this.submitting.set(false))).subscribe({
|
|
|
+ next: () => {
|
|
|
+ if (this.hasNextWordBatch) {
|
|
|
+ this.completed.set(false);
|
|
|
+ this.storageSet('_page_current_learn', currentPage + 1);
|
|
|
+ this.navigateStage('/pages/stu/stu_con');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.completed.set(false);
|
|
|
+ this.clearLearningStorage();
|
|
|
+ void this.router.navigateByUrl('/pages/member/starshop', { replaceUrl: true });
|
|
|
+ },
|
|
|
+ error: (error: ApiError) => {
|
|
|
+ this.completed.set(true);
|
|
|
+ this.platform.toast(error.message || '学习结果保存失败,请重试', 'error');
|
|
|
+ },
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (this.hasNextWordBatch) {
|
|
|
this.completed.set(false);
|
|
|
this.storageSet('_page_current_learn', currentPage + 1);
|
|
|
this.navigateStage('/pages/stu/stu_con');
|
|
|
return;
|
|
|
}
|
|
|
- const mode = String(this.storageGet('learn_mode') ?? '');
|
|
|
- if (mode === 'newword') {
|
|
|
- this.completed.set(false);
|
|
|
- this.clearLearningStorage();
|
|
|
- void this.router.navigateByUrl('/pages/member/starshop', { replaceUrl: true });
|
|
|
- } else if (mode === 'history_word') {
|
|
|
+ if (mode === 'history_word') {
|
|
|
this.completed.set(false);
|
|
|
this.clearLearningStorage();
|
|
|
void this.router.navigateByUrl('/pages/his/his', { replaceUrl: true });
|