|
@@ -3,7 +3,7 @@ import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal }
|
|
|
import { Router, RouterLink } from '@angular/router';
|
|
import { Router, RouterLink } from '@angular/router';
|
|
|
import { AlertCircle, BarChart3, BookOpen, CalendarDays, Check, ChevronRight, Clock3, Eye, EyeOff, GraduationCap, Headphones, Info, LucideAngularModule, RefreshCw, Search, Sparkles, UserRound, Zap } from 'lucide-angular';
|
|
import { AlertCircle, BarChart3, BookOpen, CalendarDays, Check, ChevronRight, Clock3, Eye, EyeOff, GraduationCap, Headphones, Info, LucideAngularModule, RefreshCw, Search, Sparkles, UserRound, Zap } from 'lucide-angular';
|
|
|
import { finalize } from 'rxjs';
|
|
import { finalize } from 'rxjs';
|
|
|
-import { buildAntiForgetView, findAntiForgetList } from '../../core/anti-forget';
|
|
|
|
|
|
|
+import { antiForgetCompleted, antiForgetDate, buildAntiForgetView, findAntiForgetList } from '../../core/anti-forget';
|
|
|
import { ApiError, ApiService } from '../../core/api.service';
|
|
import { ApiError, ApiService } from '../../core/api.service';
|
|
|
import { PlatformService } from '../../core/platform.service';
|
|
import { PlatformService } from '../../core/platform.service';
|
|
|
import { SessionService } from '../../core/session.service';
|
|
import { SessionService } from '../../core/session.service';
|
|
@@ -31,6 +31,22 @@ export class HomePageComponent implements OnInit {
|
|
|
readonly antiForgetError = signal('');
|
|
readonly antiForgetError = signal('');
|
|
|
readonly antiForgetView = computed(() => buildAntiForgetView(this.antiForgetItems()));
|
|
readonly antiForgetView = computed(() => buildAntiForgetView(this.antiForgetItems()));
|
|
|
readonly isCoach = computed(() => Number(this.session.groupId()) === 3);
|
|
readonly isCoach = computed(() => Number(this.session.groupId()) === 3);
|
|
|
|
|
+ readonly coachReviewItems = computed(() => [...this.antiForgetItems()].sort((left, right) => {
|
|
|
|
|
+ const stateOrder = (item: Item) => antiForgetCompleted(item) ? 2 : this.coachReviewReady(item) ? 0 : 1;
|
|
|
|
|
+ const stateDifference = stateOrder(left) - stateOrder(right);
|
|
|
|
|
+ if (stateDifference) return stateDifference;
|
|
|
|
|
+ const dateDifference = antiForgetDate(left).localeCompare(antiForgetDate(right));
|
|
|
|
|
+ return stateOrder(left) === 1 ? dateDifference : -dateDifference;
|
|
|
|
|
+ }));
|
|
|
|
|
+ readonly coachReviewSummary = computed(() => {
|
|
|
|
|
+ const today = this.today();
|
|
|
|
|
+ const items = this.antiForgetItems().filter((item) => antiForgetDate(item));
|
|
|
|
|
+ return {
|
|
|
|
|
+ today: items.filter((item) => antiForgetDate(item) === today).length,
|
|
|
|
|
+ due: items.filter((item) => !antiForgetCompleted(item) && antiForgetDate(item) <= today).length,
|
|
|
|
|
+ completed: items.filter(antiForgetCompleted).length,
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
readonly icons = { AlertCircle, BarChart3, BookOpen, CalendarDays, Check, ChevronRight, Clock3, Eye, EyeOff, GraduationCap, Headphones, Info, RefreshCw, Search, Sparkles, UserRound, Zap };
|
|
readonly icons = { AlertCircle, BarChart3, BookOpen, CalendarDays, Check, ChevronRight, Clock3, Eye, EyeOff, GraduationCap, Headphones, Info, RefreshCw, Search, Sparkles, UserRound, Zap };
|
|
|
|
|
|
|
|
private readonly api = inject(ApiService);
|
|
private readonly api = inject(ApiService);
|
|
@@ -41,7 +57,7 @@ export class HomePageComponent implements OnInit {
|
|
|
const uid = this.session.user()?.userId;
|
|
const uid = this.session.user()?.userId;
|
|
|
if (uid) {
|
|
if (uid) {
|
|
|
this.loadStats(uid);
|
|
this.loadStats(uid);
|
|
|
- if (!this.isCoach()) this.loadAntiForget(uid);
|
|
|
|
|
|
|
+ this.loadAntiForget(uid);
|
|
|
}
|
|
}
|
|
|
if (this.isCoach()) return;
|
|
if (this.isCoach()) return;
|
|
|
this.loadRecommendations();
|
|
this.loadRecommendations();
|
|
@@ -74,6 +90,53 @@ export class HomePageComponent implements OnInit {
|
|
|
if (uid) this.loadAntiForget(uid);
|
|
if (uid) this.loadAntiForget(uid);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ coachReviewStudent(item: Item): string {
|
|
|
|
|
+ return String(item['honeyName'] ?? item['HoneyName'] ?? item['Title'] ?? '关联学员').split('@')[0] || '关联学员';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coachReviewTitle(item: Item): string {
|
|
|
|
|
+ return String(item['kc_title'] ?? item['kctitle'] ?? item['kcmc'] ?? '单词复习').split('@')[0];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coachReviewDate(item: Item): string {
|
|
|
|
|
+ return antiForgetDate(item) || '日期待确认';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coachReviewReady(item: Item): boolean {
|
|
|
|
|
+ const date = antiForgetDate(item);
|
|
|
|
|
+ return Boolean(date && date <= this.today() && !antiForgetCompleted(item));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coachReviewStatus(item: Item): string {
|
|
|
|
|
+ if (antiForgetCompleted(item)) return '已完成';
|
|
|
|
|
+ return this.coachReviewReady(item) ? '待复习' : '未到期';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coachReviewAction(item: Item): string {
|
|
|
|
|
+ if (antiForgetCompleted(item)) return '查看';
|
|
|
|
|
+ return this.coachReviewReady(item) ? '带学生复习' : '查看计划';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ openCoachReview(item: Item): void {
|
|
|
|
|
+ if (!this.coachReviewReady(item) && !antiForgetCompleted(item)) {
|
|
|
|
|
+ void this.router.navigateByUrl('/pages/stu/stu_calendar');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const studentId = Number(item['yhid'] ?? 0);
|
|
|
|
|
+ if (!studentId) {
|
|
|
|
|
+ this.platform.toast('复习任务缺少关联学员', 'error');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.session.setStudent({
|
|
|
|
|
+ userId: studentId,
|
|
|
|
|
+ honeyName: this.coachReviewStudent(item),
|
|
|
|
|
+ groupId: 1,
|
|
|
|
|
+ });
|
|
|
|
|
+ void this.router.navigate(['/pages/stu/record'], {
|
|
|
|
|
+ queryParams: { id: item['xxjlid'], gid: item['GeneralID'], kcid: item['kcid'] },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private loadStats(uid: string | number): void {
|
|
private loadStats(uid: string | number): void {
|
|
|
this.statsLoading.set(true);
|
|
this.statsLoading.set(true);
|
|
|
this.coachStatsError.set('');
|
|
this.coachStatsError.set('');
|
|
@@ -90,7 +153,7 @@ export class HomePageComponent implements OnInit {
|
|
|
private loadAntiForget(uid: string | number): void {
|
|
private loadAntiForget(uid: string | number): void {
|
|
|
this.antiForgetLoading.set(true);
|
|
this.antiForgetLoading.set(true);
|
|
|
this.antiForgetError.set('');
|
|
this.antiForgetError.set('');
|
|
|
- this.api.post<unknown>('e_get_21list', { uid, cpage: 1, psize: 100 }).pipe(
|
|
|
|
|
|
|
+ this.api.post<unknown>('e_get_21list', { uid, shouru: this.isCoach() ? 1 : undefined, cpage: 1, psize: this.isCoach() ? 500 : 100 }).pipe(
|
|
|
finalize(() => this.antiForgetLoading.set(false)),
|
|
finalize(() => this.antiForgetLoading.set(false)),
|
|
|
).subscribe({
|
|
).subscribe({
|
|
|
next: ({ result }) => this.antiForgetItems.set(findAntiForgetList(result)),
|
|
next: ({ result }) => this.antiForgetItems.set(findAntiForgetList(result)),
|
|
@@ -98,6 +161,12 @@ export class HomePageComponent implements OnInit {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private today(): string {
|
|
|
|
|
+ const parts = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(new Date());
|
|
|
|
|
+ const value = (type: Intl.DateTimeFormatPartTypes) => parts.find((part) => part.type === type)?.value ?? '';
|
|
|
|
|
+ return `${value('year')}-${value('month')}-${value('day')}`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private toList(value: unknown): Item[] {
|
|
private toList(value: unknown): Item[] {
|
|
|
if (Array.isArray(value)) return value as Item[];
|
|
if (Array.isArray(value)) return value as Item[];
|
|
|
if (value && typeof value === 'object') {
|
|
if (value && typeof value === 'object') {
|