|
@@ -2,11 +2,11 @@ import { CommonModule } from '@angular/common';
|
|
|
import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';
|
|
import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
import { FormsModule } from '@angular/forms';
|
|
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
|
-import { ArrowLeft, BookOpen, CalendarDays, ClipboardList, Clock3, Eye, FileClock, GraduationCap, LoaderCircle, LucideAngularModule, MessageSquarePlus, RefreshCw, Save, ShieldAlert, UserRound, WalletCards } from 'lucide-angular';
|
|
|
|
|
|
|
+import { ArrowLeft, BookOpen, CalendarDays, ClipboardList, Clock3, Eye, FileChartColumn, FileClock, GraduationCap, LoaderCircle, LucideAngularModule, MessageSquarePlus, RefreshCw, Save, ShieldAlert, UserRound, WalletCards } from 'lucide-angular';
|
|
|
import { MemberAgentOption, OperationsUserDetail, ScheduleCalendarData } from '../admin.models';
|
|
import { MemberAgentOption, OperationsUserDetail, ScheduleCalendarData } from '../admin.models';
|
|
|
import { OperationsListKind, OperationsService } from '../operations.service';
|
|
import { OperationsListKind, OperationsService } from '../operations.service';
|
|
|
|
|
|
|
|
-type MemberTab = 'overview' | 'courses' | 'appointments' | 'lessons' | 'balances' | 'audit';
|
|
|
|
|
|
|
+type MemberTab = 'overview' | 'courses' | 'appointments' | 'lessons' | 'reports' | 'balances' | 'audit';
|
|
|
|
|
|
|
|
@Component({
|
|
@Component({
|
|
|
selector: 'app-operations-user-detail', standalone: true,
|
|
selector: 'app-operations-user-detail', standalone: true,
|
|
@@ -15,12 +15,12 @@ type MemberTab = 'overview' | 'courses' | 'appointments' | 'lessons' | 'balances
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
})
|
|
})
|
|
|
export class OperationsUserDetailComponent implements OnInit {
|
|
export class OperationsUserDetailComponent implements OnInit {
|
|
|
- readonly user = signal<OperationsUserDetail | null>(null); readonly tab = signal<MemberTab>('overview'); readonly related = signal<Record<MemberTab, Record<string, unknown>[]>>({ overview: [], courses: [], appointments: [], lessons: [], balances: [], audit: [] });
|
|
|
|
|
|
|
+ readonly user = signal<OperationsUserDetail | null>(null); readonly tab = signal<MemberTab>('overview'); readonly related = signal<Record<MemberTab, Record<string, unknown>[]>>({ overview: [], courses: [], appointments: [], lessons: [], reports: [], balances: [], audit: [] });
|
|
|
readonly notes = signal<Record<string, unknown>[]>([]); readonly loading = signal(true); readonly sectionLoading = signal(false); readonly saving = signal(false); readonly error = signal(''); readonly message = signal(''); readonly reason = signal('');
|
|
readonly notes = signal<Record<string, unknown>[]>([]); readonly loading = signal(true); readonly sectionLoading = signal(false); readonly saving = signal(false); readonly error = signal(''); readonly message = signal(''); readonly reason = signal('');
|
|
|
readonly editName = signal(''); readonly editNickname = signal(''); readonly editMobile = signal(''); readonly editEmail = signal(''); readonly editGender = signal(''); readonly editGrade = signal(''); readonly parentUserId = signal(0); readonly agentSearch = signal('');
|
|
readonly editName = signal(''); readonly editNickname = signal(''); readonly editMobile = signal(''); readonly editEmail = signal(''); readonly editGender = signal(''); readonly editGrade = signal(''); readonly parentUserId = signal(0); readonly agentSearch = signal('');
|
|
|
readonly balanceType = signal(1); readonly balanceDirection = signal<'add' | 'deduct'>('add'); readonly balanceAmount = signal(0); readonly payMethod = signal(''); readonly resetPassword = signal(''); readonly noteContent = signal(''); readonly revealMobile = signal(false);
|
|
readonly balanceType = signal(1); readonly balanceDirection = signal<'add' | 'deduct'>('add'); readonly balanceAmount = signal(0); readonly payMethod = signal(''); readonly resetPassword = signal(''); readonly noteContent = signal(''); readonly revealMobile = signal(false);
|
|
|
readonly courseId = signal(0); readonly courseOpenedCount = signal(1); readonly courseSearch = signal(''); readonly courseOptions = signal<ScheduleCalendarData['courses']>([]);
|
|
readonly courseId = signal(0); readonly courseOpenedCount = signal(1); readonly courseSearch = signal(''); readonly courseOptions = signal<ScheduleCalendarData['courses']>([]);
|
|
|
- readonly icons = { ArrowLeft, BookOpen, CalendarDays, ClipboardList, Clock3, Eye, FileClock, GraduationCap, LoaderCircle, MessageSquarePlus, RefreshCw, Save, ShieldAlert, UserRound, WalletCards };
|
|
|
|
|
|
|
+ readonly icons = { ArrowLeft, BookOpen, CalendarDays, ClipboardList, Clock3, Eye, FileChartColumn, FileClock, GraduationCap, LoaderCircle, MessageSquarePlus, RefreshCw, Save, ShieldAlert, UserRound, WalletCards };
|
|
|
private readonly route = inject(ActivatedRoute); private readonly operations = inject(OperationsService); private readonly objectId = this.route.snapshot.paramMap.get('objectId') || '';
|
|
private readonly route = inject(ActivatedRoute); private readonly operations = inject(OperationsService); private readonly objectId = this.route.snapshot.paramMap.get('objectId') || '';
|
|
|
readonly filteredAgents = computed(() => { const keyword = this.agentSearch().trim().toLowerCase(); return (this.user()?.agents || []).filter(agent => !keyword || `${agent.displayName} ${agent.userId}`.toLowerCase().includes(keyword)).slice(0, 30); });
|
|
readonly filteredAgents = computed(() => { const keyword = this.agentSearch().trim().toLowerCase(); return (this.user()?.agents || []).filter(agent => !keyword || `${agent.displayName} ${agent.userId}`.toLowerCase().includes(keyword)).slice(0, 30); });
|
|
|
readonly filteredCourses = computed(() => { const keyword = this.courseSearch().trim().toLowerCase(); return this.courseOptions().filter(course => !keyword || `${course.courseName} ${course.courseId}`.toLowerCase().includes(keyword)).slice(0, 30); });
|
|
readonly filteredCourses = computed(() => { const keyword = this.courseSearch().trim().toLowerCase(); return this.courseOptions().filter(course => !keyword || `${course.courseName} ${course.courseId}`.toLowerCase().includes(keyword)).slice(0, 30); });
|
|
@@ -38,6 +38,7 @@ export class OperationsUserDetailComponent implements OnInit {
|
|
|
const user = this.user(); if (!user || tab === 'overview') return; this.sectionLoading.set(true); this.error.set('');
|
|
const user = this.user(); if (!user || tab === 'overview') return; this.sectionLoading.set(true); this.error.set('');
|
|
|
try {
|
|
try {
|
|
|
if (tab === 'audit') { const [notes, audit] = await Promise.all([this.operations.memberRows('ops/users/notes', user.objectId), this.operations.memberRows('ops/users/audit', user.objectId)]); this.notes.set(notes.items); this.related.update(value => ({ ...value, audit: audit.items })); }
|
|
if (tab === 'audit') { const [notes, audit] = await Promise.all([this.operations.memberRows('ops/users/notes', user.objectId), this.operations.memberRows('ops/users/audit', user.objectId)]); this.notes.set(notes.items); this.related.update(value => ({ ...value, audit: audit.items })); }
|
|
|
|
|
+ else if (tab === 'reports') { const page = await this.operations.learningReports(1, 100, '', { studentObjectId: user.objectId }, false); this.related.update(value => ({ ...value, reports: page.items })); }
|
|
|
else { const kind = this.kindFor(tab); if (kind) { const page = await this.operations.list(kind, 1, 100, String(user.userId)); this.related.update(value => ({ ...value, [tab]: page.items })); } }
|
|
else { const kind = this.kindFor(tab); if (kind) { const page = await this.operations.list(kind, 1, 100, String(user.userId)); this.related.update(value => ({ ...value, [tab]: page.items })); } }
|
|
|
if (tab === 'courses' && user.permissions.canEdit && !this.courseOptions().length) { const today = new Date().toISOString().slice(0, 10); const schedule = await this.operations.schedule(today, today); this.courseOptions.set(schedule.courses); }
|
|
if (tab === 'courses' && user.permissions.canEdit && !this.courseOptions().length) { const today = new Date().toISOString().slice(0, 10); const schedule = await this.operations.schedule(today, today); this.courseOptions.set(schedule.courses); }
|
|
|
} catch (error) { this.error.set(error instanceof Error ? error.message : '业务记录加载失败'); }
|
|
} catch (error) { this.error.set(error instanceof Error ? error.message : '业务记录加载失败'); }
|