admin-login.component.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { CommonModule } from '@angular/common';
  2. import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
  3. import { FormsModule } from '@angular/forms';
  4. import { ActivatedRoute, Router } from '@angular/router';
  5. import { Eye, EyeOff, LoaderCircle, LockKeyhole, LucideAngularModule, UserRound } from 'lucide-angular';
  6. import { AdminSessionService } from '../admin-session.service';
  7. import { CloudFunctionError, CloudFunctionsService } from '../cloud-functions.service';
  8. import { AdminIdentity } from '../admin.models';
  9. import { OperationsService } from '../operations.service';
  10. @Component({
  11. selector: 'app-admin-login',
  12. standalone: true,
  13. imports: [CommonModule, FormsModule, LucideAngularModule],
  14. templateUrl: './admin-login.component.html',
  15. styleUrl: './admin-login.component.scss',
  16. changeDetection: ChangeDetectionStrategy.OnPush,
  17. })
  18. export class AdminLoginComponent {
  19. username = '';
  20. password = '';
  21. readonly showPassword = signal(false);
  22. readonly loading = signal(false);
  23. readonly error = signal('');
  24. readonly notice = signal('');
  25. readonly icons = { Eye, EyeOff, LoaderCircle, LockKeyhole, UserRound };
  26. private readonly sessions = inject(AdminSessionService);
  27. private readonly functions = inject(CloudFunctionsService);
  28. private readonly router = inject(Router);
  29. private readonly route = inject(ActivatedRoute);
  30. private readonly operations = inject(OperationsService);
  31. constructor() { this.username = this.route.snapshot.queryParamMap.get('account')?.trim().slice(0,100) || ''; document.title = '后台登录 · 小树陪练'; if (this.route.snapshot.queryParamMap.get('passwordChanged') === '1') this.notice.set('密码已修改,所有旧会话均已注销,请使用新密码登录。'); }
  32. async submit(): Promise<void> {
  33. if (!this.username.trim() || !this.password) {
  34. this.error.set('请输入管理员账号和密码');
  35. return;
  36. }
  37. this.loading.set(true);
  38. this.error.set('');
  39. let attemptToken = '';
  40. try {
  41. attemptToken = (await this.sessions.login(this.username, this.password)).sessionToken;
  42. const returnUrl = this.sessions.session()?.passwordResetRequired ? '/admin/change-password' : this.route.snapshot.queryParamMap.get('returnUrl') || '/admin/dashboard';
  43. if (this.sessions.session()?.isSuperAdmin && returnUrl.split('?')[0] === '/admin/dashboard') {
  44. void this.operations.dashboard().catch(() => undefined);
  45. void import('../layout/admin-shell.component');
  46. void import('./admin-dashboard.component');
  47. }
  48. const meta = await this.functions.admin<{ identity: AdminIdentity }>('meta');
  49. this.sessions.applyIdentity(meta.identity);
  50. await this.router.navigateByUrl(meta.identity.operationsRole==='store-admin'&&!this.sessions.session()?.passwordResetRequired?'/admin/store-home':returnUrl);
  51. } catch (error) {
  52. // Transient metadata/network failures must not revoke a successful login.
  53. if (error instanceof CloudFunctionError && error.status === 403 && attemptToken && this.sessions.session()?.sessionToken === attemptToken) this.sessions.logout(false);
  54. this.error.set(error instanceof Error ? error.message : '登录失败');
  55. } finally {
  56. this.loading.set(false);
  57. }
  58. }
  59. }