Просмотр исходного кода

fix(mobile): finish coach learning without appointment

彭峰 1 месяц назад
Родитель
Сommit
9f9c9e93c0

+ 13 - 1
projects/xiaoshu-mobile/src/app/core/home-learning-modules.integration.spec.ts

@@ -16,6 +16,7 @@ class LearningModulesApiService {
   lastWordParams: Record<string, unknown> = {};
   lastLearningWordUpdate: Record<string, unknown> = {};
   lastMemoryParams: Record<string, unknown> = {};
+  lastStandaloneLearningRecord: Record<string, unknown> = {};
 
   get<T>(action: string, params: Record<string, unknown> = {}): Observable<ApiEnvelope<T>> {
     if (action === 'content_list' && Number(params['nid']) === 291) {
@@ -97,6 +98,10 @@ class LearningModulesApiService {
       this.lastLearningWordUpdate = params;
       return this.result<T>({ affected: String(params['wordsId'] ?? '').split(',').filter(Boolean).length });
     }
+    if (action === 'content_add_zt') {
+      this.lastStandaloneLearningRecord = params;
+      return this.result<T>({ GeneralID: 9901 });
+    }
     if (action === 'e_order_tongji') {
       return this.result<T>({ t30: 4, t60: 6, t_tiyan: 1, t_total: 11, t_shichang: 9, t_yongji: 320 });
     }
@@ -642,7 +647,14 @@ describe('home learning modules', () => {
     (spellingActions.querySelectorAll('button')[1] as HTMLButtonElement).click();
     harness.fixture.detectChanges();
     expect(harness.routeNativeElement?.textContent).toContain('拼写完成');
-  });
+    (harness.routeNativeElement?.querySelector('.compact-report .primary-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(router.url).toBe('/pages/his/his');
+    const standaloneRecord = (TestBed.inject(ApiService) as unknown as LearningModulesApiService).lastStandaloneLearningRecord;
+    expect(JSON.parse(String(standaloneRecord['content']))).toEqual(jasmine.objectContaining({ ModelID: 56, Hits: 1, title: '张雅茜' }));
+    expect(JSON.parse(String(standaloneRecord['addon']))).toEqual(jasmine.objectContaining({ UserID: 100, pl: 300, learned: 1 }));
+  }, 20000);
 
   it('counts words marked as known as completed and persists them for the student', async () => {
     user.set({ userId: 300, userName: 'coach', honeyName: '张老师', groupId: 3 });

+ 4 - 1
projects/xiaoshu-mobile/src/app/features/learning/learning-page.component.html

@@ -491,6 +491,9 @@
     <div class="report-icon"><lucide-icon [img]="icons.CheckCheck" [size]="34" /></div>
     <h2>{{ title }}</h2>
     <p>{{ text }}</p>
-    <button type="button" class="primary-button" (click)="continueAfterStage()">继续下一关<lucide-icon [img]="icons.ChevronRight" [size]="18" /></button>
+    <button type="button" class="primary-button" [disabled]="submitting()" (click)="continueAfterStage()">
+      {{ submitting() ? '正在保存…' : '继续下一关' }}
+      <lucide-icon [img]="icons.ChevronRight" [size]="18" />
+    </button>
   </div>
 </ng-template>

+ 53 - 2
projects/xiaoshu-mobile/src/app/features/learning/learning-page.component.ts

@@ -729,10 +729,11 @@ export class LearningPageComponent implements OnInit, OnDestroy {
   }
 
   continueAfterStage(): void {
-    this.completed.set(false);
     if (this.isMatchingPage) {
+      this.completed.set(false);
       this.navigateStage('/pages/stu/stu4');
     } else if (this.isListeningPage) {
+      this.completed.set(false);
       const path = Number(this.session.user()?.groupId) === 3 ? '/pages/stu/pl_stu5' : '/pages/stu/stu5';
       this.navigateStage(path);
     } else if (this.isSpellingPage) {
@@ -1245,7 +1246,11 @@ export class LearningPageComponent implements OnInit, OnDestroy {
     } else if (mode === 'history_word') {
       void this.router.navigateByUrl('/pages/his/his');
     } else if (this.meta.path === 'pages/stu/pl_stu5') {
-      this.createPracticeLearningRecord();
+      if (this.query('orderId')) {
+        this.createPracticeLearningRecord();
+      } else {
+        this.createStandalonePracticeLearningRecord();
+      }
     } else {
       this.navigateStage('/pages/stu/stu6');
     }
@@ -1479,6 +1484,52 @@ export class LearningPageComponent implements OnInit, OnDestroy {
     });
   }
 
+  private createStandalonePracticeLearningRecord(): void {
+    const allWords = this.readAllLearningWords();
+    const user = this.session.user();
+    const student = this.session.student();
+    if (Number(user?.groupId) === 3 && !student?.userId) {
+      this.platform.toast('请先选择学员', 'error');
+      return;
+    }
+    const owner = Number(user?.groupId) === 3 ? student : user;
+    const xxqs = allWords.map((word) => ({ GeneralID: word.wordId, Title: word.word, check: 0 }));
+    const content: SourceItem = {
+      ModelID: 56,
+      nodeId: 291,
+      inputer: user?.honeyName,
+      status: 99,
+      Hits: xxqs.length,
+      title: owner?.honeyName,
+    };
+    const addon: SourceItem = {
+      con: `完成了${xxqs.length}个单词的学习@${this.dateTime()}`,
+      dqrq: this.compactDate(),
+      pl: Number(user?.groupId) === 3 ? user?.userId : 0,
+      UserID: owner?.userId,
+      learned: xxqs.length,
+      ygg: 0,
+      djq: 0,
+      xxqs: JSON.stringify(xxqs),
+      fxrl: '',
+    };
+    this.submitting.set(true);
+    this.persistWordsAsLearned(allWords, owner).pipe(
+      switchMap(() => this.api.post('content_add_zt', {
+        content: JSON.stringify(content),
+        addon: JSON.stringify(addon),
+      })),
+      finalize(() => this.submitting.set(false)),
+    ).subscribe({
+      next: () => {
+        this.clearLearningStorage();
+        this.platform.toast('学习记录已保存', 'success');
+        void this.router.navigateByUrl('/pages/his/his');
+      },
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
   private loadRecord(): void {
     this.api.post<unknown>('e_record_detail', { id: this.query('id') }).pipe(finalize(() => this.loading.set(false))).subscribe({
       next: ({ result, addon }) => {