import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core'; import { Router, RouterLink } from '@angular/router'; import { Award, BookMarked, ChevronRight, CircleHelp, History, LogOut, LucideAngularModule, MessageSquare, Settings, ShieldCheck, UserRound } from 'lucide-angular'; import { finalize } from 'rxjs'; import { APP_VERSION } from '../../core/app.constants'; import { ApiService } from '../../core/api.service'; import { AntiForgetHistorySummary } from '../../core/anti-forget'; import { AppUser } from '../../core/models'; import { SessionService } from '../../core/session.service'; import { buildAccountMembership, CompanionLearningSummary } from './account-membership'; interface CoachPanelSummary { todaySchedules: number; completedLessons: number; reviewIncome: number; reviewRecords: number; totalLessons: number; totalHours: number; course30: number; course60: number; trialLessons: number; } interface AccountOverview { profile: Partial; coachPanel?: CoachPanelSummary; companionSummary?: Pick; antiForgetSummary?: AntiForgetHistorySummary; refreshedAt: string; } @Component({ selector: 'app-account-page', standalone: true, imports: [CommonModule, RouterLink, LucideAngularModule], templateUrl: './account-page.component.html', styleUrl: './account-page.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccountPageComponent implements OnInit { readonly session = inject(SessionService); readonly version = APP_VERSION; readonly coachPanel = signal({ todaySchedules: 0, completedLessons: 0, reviewIncome: 0, reviewRecords: 0, totalLessons: 0, totalHours: 0, course30: 0, course60: 0, trialLessons: 0 }); readonly coachPanelLoading = signal(false); readonly antiForgetLoading = signal(false); readonly antiForgetError = signal(''); readonly avatarFailed = signal(false); readonly companionLoading = signal(false); readonly antiForgetSummary = signal({ total: 0, completed: 0, progress: 0, todayTotal: 0, todayCompleted: 0, duePending: 0, scheduled: 0, scheduledDays: 0 }); readonly membership = computed(() => buildAccountMembership(this.session.user())); readonly companionSummary = signal({ completedSessions: 0, completedMinutes: 0, durationLabel: '0 小时' }); readonly icons = { Award, BookMarked, ChevronRight, CircleHelp, History, LogOut, MessageSquare, Settings, ShieldCheck, UserRound }; readonly services = [ { path: '/pages/his/well', label: '专项训练', icon: Award }, { path: '/pages/member/starshop', label: '生词本', icon: BookMarked }, { path: '/pages/his/his', label: '学习记录', icon: History }, { path: '/pages/member/feedback', label: '意见反馈', icon: MessageSquare }, ]; private readonly api = inject(ApiService); private readonly router = inject(Router); ngOnInit(): void { const uid = this.session.user()?.userId; if (!uid) return; this.loadAccountOverview(false); } loadAntiForget(): void { this.loadAccountOverview(true); } private loadAccountOverview(refresh: boolean): void { const uid = this.session.user()?.userId; if (!uid) return; const coach = Number(this.session.groupId()) === 3; this.coachPanelLoading.set(coach); this.companionLoading.set(!coach); this.antiForgetLoading.set(!coach); this.antiForgetError.set(''); this.api.post('app_account_overview', { uid, refresh }).pipe( finalize(() => { this.coachPanelLoading.set(false); this.companionLoading.set(false); this.antiForgetLoading.set(false); }), ).subscribe({ next: ({ result }) => { if (!result) return; this.session.patchUser(result.profile ?? {}); if (result.coachPanel) this.coachPanel.set(result.coachPanel); if (result.companionSummary) { const minutes = Number(result.companionSummary.completedMinutes ?? 0); this.companionSummary.set({ ...result.companionSummary, durationLabel: this.formatMinutes(minutes) }); } if (result.antiForgetSummary) this.antiForgetSummary.set(result.antiForgetSummary); }, error: () => this.antiForgetError.set('抗遗忘数据暂时无法加载'), }); } logout(): void { this.session.logout(); void this.router.navigateByUrl('/pages/member/wxauth'); } avatarUrl(): string { return this.api.assetUrl(this.session.user()?.userFace); } private formatMinutes(minutes: number): string { if (!minutes) return '0 小时'; const hours = Math.floor(minutes / 60); const remainder = minutes % 60; if (!hours) return `${remainder} 分钟`; return remainder ? `${hours} 小时 ${remainder} 分钟` : `${hours} 小时`; } }