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

refine(admin): require report preview before PDF download

彭峰 2 недель назад
Родитель
Сommit
46b588593b

+ 1 - 1
projects/xiaoshu-admin/src/app/pages/operations-learning-reports.component.html

@@ -42,7 +42,7 @@
     <header><div><strong>学习报表明细</strong><span>共 {{ pageData.total }} 份,点击查看完整单词与复习计划</span></div></header>
     <div class="table-scroll"><table class="data-table report-table"><thead><tr><th>学习日期</th><th>学员</th><th>陪练老师</th><th>课程</th><th>学习结果</th><th>掌握率</th><th>状态</th><th>操作</th></tr></thead><tbody>
       @for (row of pageData.items; track trackReport($index, row)) {
-        <tr><td><strong>{{ row.studyDate || '日期缺失' }}</strong><small>@if(row.appointmentAt){ {{ row.appointmentAt | date:'HH:mm' }} } @else {记录 {{ row.generalId }}}</small></td><td><strong>{{ row.studentName }}</strong><small>ID {{ row.studentId }} · {{ row.studentMobileMasked || '无手机号' }}</small></td><td>{{ row.coachName }}<small>@if(row.coachId){ ID {{ row.coachId }} }</small></td><td>{{ row.courseName }}<small>{{ row.classTypeLabel }}</small></td><td><strong>{{ row.learnedCount }} 个</strong><small>掌握 {{ row.masteredCount }} · 复习 {{ row.reviewDueCount }} · 未标记 {{ row.unmarkedCount }}</small></td><td><div class="rate"><span [style.width.%]="row.masteryRate"></span></div><strong>{{ row.masteryRate | number:'1.0-1' }}%</strong></td><td><span class="report-status" [attr.data-status]="row.status">{{ row.statusLabel }}</span></td><td><div class="row-actions"><button type="button" title="查看报表" aria-label="查看报表" (click)="open(row.targetId)"><lucide-icon [img]="icons.Eye" [size]="16" /></button><button type="button" title="下载PDF" aria-label="下载PDF" [disabled]="!row.canExport || exportingId()===row.targetId" (click)="download(row)"><lucide-icon [img]="exportingId()===row.targetId ? icons.LoaderCircle : icons.Download" [size]="16" [class.spin]="exportingId()===row.targetId" /></button></div></td></tr>
+        <tr><td><strong>{{ row.studyDate || '日期缺失' }}</strong><small>@if(row.appointmentAt){ {{ row.appointmentAt | date:'HH:mm' }} } @else {记录 {{ row.generalId }}}</small></td><td><strong>{{ row.studentName }}</strong><small>ID {{ row.studentId }} · {{ row.studentMobileMasked || '无手机号' }}</small></td><td>{{ row.coachName }}<small>@if(row.coachId){ ID {{ row.coachId }} }</small></td><td>{{ row.courseName }}<small>{{ row.classTypeLabel }}</small></td><td><strong>{{ row.learnedCount }} 个</strong><small>掌握 {{ row.masteredCount }} · 复习 {{ row.reviewDueCount }} · 未标记 {{ row.unmarkedCount }}</small></td><td><div class="rate"><span [style.width.%]="row.masteryRate"></span></div><strong>{{ row.masteryRate | number:'1.0-1' }}%</strong></td><td><span class="report-status" [attr.data-status]="row.status">{{ row.statusLabel }}</span></td><td><div class="row-actions"><button type="button" title="查看报表" aria-label="查看报表" (click)="open(row.targetId)"><lucide-icon [img]="icons.Eye" [size]="16" /></button></div></td></tr>
       } @empty { <tr><td colspan="8"><div class="empty">当前筛选范围暂无学习报表</div></td></tr> }
     </tbody></table></div>
     <footer class="pagination"><span>第 {{ pageData.page }} / {{ totalPages() }} 页</span><div><button type="button" aria-label="上一页" [disabled]="pageData.page<=1" (click)="changePage(pageData.page-1)"><lucide-icon [img]="icons.ChevronLeft" [size]="17" /></button><button type="button" aria-label="下一页" [disabled]="pageData.page>=totalPages()" (click)="changePage(pageData.page+1)"><lucide-icon [img]="icons.ChevronRight" [size]="17" /></button></div></footer>

+ 10 - 0
projects/xiaoshu-admin/src/app/pages/operations-learning-reports.component.spec.ts

@@ -47,6 +47,16 @@ describe('OperationsLearningReportsComponent', () => {
     expect((fixture.nativeElement as HTMLElement).textContent).toContain('21天复习计划');
   });
 
+  it('offers download only after opening the preview and choosing a report version', async () => {
+    const element: HTMLElement = fixture.nativeElement;
+    expect(element.querySelector('.report-table button[aria-label="下载PDF"]')).toBeNull();
+    expect(element.querySelectorAll('.report-table button[aria-label="查看报表"]').length).toBe(1);
+    await fixture.componentInstance.open('study-common-1');
+    fixture.detectChanges();
+    expect(element.querySelector('.report-variants')).not.toBeNull();
+    expect(element.querySelector('.report-drawer button.ui-button.primary')?.textContent).toContain('下载 PDF');
+  });
+
   it('submits every visible filter when the operator queries reports', async () => {
     const component = fixture.componentInstance;
     operations.learningReports.calls.reset();

+ 1 - 1
projects/xiaoshu-admin/src/app/pages/operations-learning-reports.component.ts

@@ -229,7 +229,7 @@ export class OperationsLearningReportsComponent implements OnInit, OnDestroy {
     this.reportVariant.set(variant);
   }
 
-  async download(row: LearningReportListItem | LearningReportDetail, variant: ReportVariant = 'full'): Promise<void> {
+  async download(row: LearningReportDetail, variant: ReportVariant): Promise<void> {
     if (!row.canExport || this.exportingId() || this.detailLoading()) return;
     this.exportingId.set(row.targetId); this.error.set('');
     try {