|
@@ -126,6 +126,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
readonly newWordCounts = signal({ pending: 0, completed: 0 });
|
|
readonly newWordCounts = signal({ pending: 0, completed: 0 });
|
|
|
private readonly newWordPageSize = 30;
|
|
private readonly newWordPageSize = 30;
|
|
|
private readonly newWordSelectionCache = new Map<string, Word>();
|
|
private readonly newWordSelectionCache = new Map<string, Word>();
|
|
|
|
|
+ private legacyNewWords: Word[] | null = null;
|
|
|
readonly currentIndex = signal(0);
|
|
readonly currentIndex = signal(0);
|
|
|
readonly revealed = signal(false);
|
|
readonly revealed = signal(false);
|
|
|
readonly completed = signal(false);
|
|
readonly completed = signal(false);
|
|
@@ -1198,15 +1199,60 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
this.selectionPage.set(1);
|
|
this.selectionPage.set(1);
|
|
|
this.selectionTotal.set(this.newWordCounts()[bucket]);
|
|
this.selectionTotal.set(this.newWordCounts()[bucket]);
|
|
|
this.words.set([]);
|
|
this.words.set([]);
|
|
|
|
|
+ if (this.legacyNewWords) { this.showLegacyNewWordPage(); return; }
|
|
|
this.loadSelection();
|
|
this.loadSelection();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
goToNewWordPage(page: number): void {
|
|
goToNewWordPage(page: number): void {
|
|
|
if (this.selectionLoading() || page < 1 || page > this.newWordPageCount() || page === this.selectionPage()) return;
|
|
if (this.selectionLoading() || page < 1 || page > this.newWordPageCount() || page === this.selectionPage()) return;
|
|
|
this.selectionPage.set(page);
|
|
this.selectionPage.set(page);
|
|
|
|
|
+ if (this.legacyNewWords) { this.showLegacyNewWordPage(); return; }
|
|
|
this.loadSelection();
|
|
this.loadSelection();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private showLegacyNewWordPage(): void {
|
|
|
|
|
+ const all = this.legacyNewWords ?? [];
|
|
|
|
|
+ const pending = all.filter((word) => !this.newWordLearned(word));
|
|
|
|
|
+ const completed = all.filter((word) => this.newWordLearned(word));
|
|
|
|
|
+ const bucket = this.activeSelectionBucket() === 'completed' ? completed : pending;
|
|
|
|
|
+ const start = (this.selectionPage() - 1) * this.newWordPageSize;
|
|
|
|
|
+ this.newWordCounts.set({ pending: pending.length, completed: completed.length });
|
|
|
|
|
+ this.selectionTotal.set(bucket.length);
|
|
|
|
|
+ this.words.set(bucket.slice(start, start + this.newWordPageSize));
|
|
|
|
|
+ for (const word of all) this.newWordSelectionCache.set(word.id, word);
|
|
|
|
|
+ this.error.set('');
|
|
|
|
|
+ this.selectionLoading.set(false);
|
|
|
|
|
+ this.loading.set(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private loadLegacyNewWords(uid: number | undefined, page = 1, collected: Word[] = []): void {
|
|
|
|
|
+ const size = 500;
|
|
|
|
|
+ this.selectionRequest = this.api.get<unknown>('e_words_list', { uid, cpage: page, psize: size }).subscribe({
|
|
|
|
|
+ next: ({ result, page: pageInfo }) => {
|
|
|
|
|
+ const incoming = this.findList(result).map((item) => this.toNewWord(item)).filter((word) => word.word);
|
|
|
|
|
+ const all = [...collected, ...incoming];
|
|
|
|
|
+ const total = Number(pageInfo?.itemCount ?? all.length);
|
|
|
|
|
+ if (!Number.isFinite(total) || (!incoming.length && all.length < total)) {
|
|
|
|
|
+ this.error.set('生词本数据未加载完整,请重试');
|
|
|
|
|
+ this.selectionLoading.set(false);
|
|
|
|
|
+ this.loading.set(false);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (all.length < total) {
|
|
|
|
|
+ this.loadLegacyNewWords(uid, page + 1, all);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.legacyNewWords = all;
|
|
|
|
|
+ this.showLegacyNewWordPage();
|
|
|
|
|
+ },
|
|
|
|
|
+ error: (error: ApiError) => {
|
|
|
|
|
+ this.error.set(error.message || '生词本加载失败,请重试');
|
|
|
|
|
+ this.selectionLoading.set(false);
|
|
|
|
|
+ this.loading.set(false);
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private loadSelection(more=false): void {
|
|
private loadSelection(more=false): void {
|
|
|
const accept=(result:unknown,total:unknown)=>{const incoming=this.findList(result).map(item=>this.meta.path==='pages/member/starshop'?this.toNewWord(item):this.toWord(item)).filter(w=>w.word);this.words.set(more?[...this.words(),...incoming]:incoming);this.selectionTotal.set(Number(total)||this.words().length);};
|
|
const accept=(result:unknown,total:unknown)=>{const incoming=this.findList(result).map(item=>this.meta.path==='pages/member/starshop'?this.toNewWord(item):this.toWord(item)).filter(w=>w.word);this.words.set(more?[...this.words(),...incoming]:incoming);this.selectionTotal.set(Number(total)||this.words().length);};
|
|
|
const failure=(error:ApiError)=>{if(more){this.selectionPage.update(p=>p-1);this.platform.toast(error.message,'error');}else this.error.set(error.message);};
|
|
const failure=(error:ApiError)=>{if(more){this.selectionPage.update(p=>p-1);this.platform.toast(error.message,'error');}else this.error.set(error.message);};
|
|
@@ -1232,17 +1278,33 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
|
|
const owner = Number(this.session.user()?.groupId) === 3 ? this.session.student() : this.session.user();
|
|
const owner = Number(this.session.user()?.groupId) === 3 ? this.session.student() : this.session.user();
|
|
|
this.selectionLoading.set(true);
|
|
this.selectionLoading.set(true);
|
|
|
- this.selectionRequest=this.api.get<unknown>('e_words_list',{uid:owner?.userId,cpage:this.selectionPage(),psize:this.newWordPageSize,learningStatus:this.activeSelectionBucket()}).pipe(finalize(()=>{this.selectionLoading.set(false);this.loading.set(false);})).subscribe({
|
|
|
|
|
|
|
+ this.selectionRequest=this.api.get<unknown>('e_words_list',{uid:owner?.userId,cpage:this.selectionPage(),psize:this.newWordPageSize,learningStatus:this.activeSelectionBucket()}).subscribe({
|
|
|
next: ({result,page}) => {
|
|
next: ({result,page}) => {
|
|
|
const incoming = this.findList(result).map((item) => this.toNewWord(item)).filter((word) => word.word);
|
|
const incoming = this.findList(result).map((item) => this.toNewWord(item)).filter((word) => word.word);
|
|
|
|
|
+ const pendingCount = Number(page?.['pendingCount']);
|
|
|
|
|
+ const completedCount = Number(page?.['completedCount']);
|
|
|
|
|
+ const total = Number(page?.itemCount ?? incoming.length);
|
|
|
|
|
+ const validCounts = Number.isFinite(pendingCount) && Number.isFinite(completedCount)
|
|
|
|
|
+ && pendingCount >= 0 && completedCount >= 0 && pendingCount + completedCount >= total
|
|
|
|
|
+ && (incoming.length > 0 || total === 0 || this.selectionPage() > Math.ceil(total / this.newWordPageSize));
|
|
|
|
|
+ if (!validCounts) {
|
|
|
|
|
+ this.loadLegacyNewWords(owner?.userId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
this.words.set(incoming);
|
|
this.words.set(incoming);
|
|
|
for (const word of incoming) this.newWordSelectionCache.set(word.id, word);
|
|
for (const word of incoming) this.newWordSelectionCache.set(word.id, word);
|
|
|
- const counts = {pending: Number(page?.['pendingCount'] ?? 0), completed: Number(page?.['completedCount'] ?? 0)};
|
|
|
|
|
|
|
+ const counts = {pending: pendingCount, completed: completedCount};
|
|
|
this.newWordCounts.set(counts);
|
|
this.newWordCounts.set(counts);
|
|
|
- this.selectionTotal.set(Number(page?.itemCount ?? counts[this.activeSelectionBucket()]));
|
|
|
|
|
|
|
+ this.selectionTotal.set(total);
|
|
|
this.error.set('');
|
|
this.error.set('');
|
|
|
|
|
+ this.selectionLoading.set(false);
|
|
|
|
|
+ this.loading.set(false);
|
|
|
|
|
+ },
|
|
|
|
|
+ error: (error: ApiError) => {
|
|
|
|
|
+ failure(error);
|
|
|
|
|
+ this.selectionLoading.set(false);
|
|
|
|
|
+ this.loading.set(false);
|
|
|
},
|
|
},
|
|
|
- error: failure,
|
|
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1977,6 +2039,7 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
this.selected.set(new Set());
|
|
this.selected.set(new Set());
|
|
|
this.newWordSelectionCache.clear();
|
|
this.newWordSelectionCache.clear();
|
|
|
this.newWordCounts.set({ pending: 0, completed: 0 });
|
|
this.newWordCounts.set({ pending: 0, completed: 0 });
|
|
|
|
|
+ this.legacyNewWords = null;
|
|
|
this.selectionRevealed.set(new Set());
|
|
this.selectionRevealed.set(new Set());
|
|
|
this.selectionAssessment.set({});
|
|
this.selectionAssessment.set({});
|
|
|
this.activeSelectionBucket.set('pending');
|
|
this.activeSelectionBucket.set('pending');
|