lesson-report.component.html 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <section class="lesson-report" [attr.aria-busy]="loading()">
  2. @if (error()) { <div class="load-error" role="alert"><p>{{ error() }}</p><button type="button" (click)="load()" [disabled]="loading() || exporting()">重新加载</button></div> }
  3. @if (report(); as r) {
  4. <header class="report-hero">
  5. <div class="hero-top"><span>本次课堂报告</span><button type="button" aria-label="刷新学习报告" (click)="load()" [disabled]="loading() || exporting()"><lucide-icon [img]="icons.RefreshCw" [size]="18" /></button></div>
  6. <h1>{{ r.order['kcmc'] || r.order['yykcmc'] || '课程学习报告' }}</h1>
  7. <p>{{ r.order['HoneyName'] || '学员' }}<span aria-hidden="true"> · </span>{{ dateLabel(r.order['yysj']) }}</p>
  8. @if (r.order['cancelled']) { <span class="status-badge">此预约已取消</span> }
  9. @else { <span class="status-badge">{{ r.feedback && r.feedback['feedbackStatus'] !== 'pending' ? '老师反馈已提交' : '老师反馈待补' }}</span> }
  10. </header>
  11. <div class="learning-stats" aria-label="本次学习统计">
  12. <div><strong>{{ count('learned') }}</strong><span>学习单词</span></div>
  13. <div><strong>{{ count('ygg') }}</strong><span>已掌握</span></div>
  14. <div><strong>{{ r.antiForget.completed }}<small> / {{ r.antiForget.total }}</small></strong><span>抗遗忘完成</span></div>
  15. </div>
  16. <section class="report-card">
  17. <header><h2><lucide-icon [img]="icons.ClipboardList" [size]="19" aria-hidden="true" />完课反馈</h2>
  18. @if ($any(r.order['allowedActions'])?.includes('editFeedback')) { <a class="edit-link" routerLink="/pages/shop/get_order" [queryParams]="{id:orderId,editReport:1}">{{ !r.feedback || r.feedback['feedbackStatus'] === 'pending' ? '补充反馈' : '修改报告' }}</a> }
  19. </header>
  20. @if (r.order['feedbackLocked']) { <p class="empty-copy" role="status">学生已评价,课堂反馈已锁定</p> }
  21. @if (r.feedback && r.feedback['feedbackStatus'] !== 'pending') { <app-lesson-feedback [feedback]="r.feedback" /> }
  22. @else { <p class="empty-copy">老师尚未提交反馈,提交后将在这里展示。</p> }
  23. </section>
  24. <section class="report-card">
  25. <header><h2><lucide-icon [img]="icons.BookOpen" [size]="19" aria-hidden="true" />学习记录</h2><span>{{ r.records.length }} 条记录</span></header>
  26. @if (sources().length) { <p class="empty-copy" aria-label="课堂词表来源">@for (source of sources(); track $index) { {{ $index ? '+' : '' }}{{ source.label }} {{ source.count }} 词 } = {{ count('learned') }} 词</p> }
  27. <div class="study-records">
  28. @for (item of r.records; track $index) {
  29. <article><time>{{ dateLabel(item['dqrq']) }}</time><p>{{ item['con'] || '单词学习' }}</p><div><span>已学 <b>{{ item['learned'] || 0 }}</b> 词</span><span>已掌握 <b>{{ item['ygg'] || 0 }}</b> 词</span><span>待加强 <b>{{ item['djq'] || 0 }}</b> 词</span></div></article>
  30. } @empty { <p class="empty-copy">暂无本次课程的单词学习记录</p> }
  31. </div>
  32. </section>
  33. <section class="report-card">
  34. <header><h2><lucide-icon [img]="icons.Star" [size]="19" aria-hidden="true" />学生评价</h2></header>
  35. @if (r.order['studentReview']; as review) { <p class="rating">{{ $any(review).score }}<small> / 5 分</small></p><p class="review-comment">{{ $any(review).comment || '学员未填写文字评价' }}</p> }
  36. @else { <p class="empty-copy">等待学员评价本次课程</p> }
  37. </section>
  38. <section class="report-card">
  39. <header><h2><lucide-icon [img]="icons.BrainCircuit" [size]="19" aria-hidden="true" />抗遗忘计划</h2><span>{{ reviewProgress() }}%</span></header>
  40. <div class="review-track" role="progressbar" aria-label="抗遗忘计划完成进度" [attr.aria-valuenow]="reviewProgress()" aria-valuemin="0" aria-valuemax="100"><span [style.width.%]="reviewProgress()"></span></div>
  41. <p class="empty-copy">{{ r.antiForget.total ? '已完成 ' + r.antiForget.completed + ' 项,共 ' + r.antiForget.total + ' 项复习任务' : '本次课程暂无抗遗忘任务' }}</p>
  42. </section>
  43. <p class="report-updated">数据更新于 {{ dateLabel(r.refreshedAt) }}</p>
  44. } @else if (loading()) { <div class="loading-state" role="status">正在整理本次学习报告…</div> }
  45. <footer>
  46. @if (session.groupId() === 3) {
  47. <fieldset class="pdf-options" [disabled]="exporting()"><legend>选择下载内容</legend>
  48. @for (variant of pdfVariants; track variant.key) {
  49. <label><input type="checkbox" [checked]="selectedVariants().includes(variant.key)" (change)="toggleVariant(variant.key)" />{{ variant.label }}</label>
  50. }
  51. </fieldset>
  52. <button class="download-pdf" type="button" (click)="downloadPdf()" [disabled]="!report() || loading() || exporting() || !selectedVariants().length">
  53. <lucide-icon [img]="exporting() ? icons.LoaderCircle : icons.Download" [class.spin]="exporting()" [size]="19" />{{ exporting() ? exportProgress() : '下载 PDF(' + selectedVariants().length + '份)' }}
  54. </button>
  55. }
  56. <a routerLink="/pages/his/his">查看学习记录</a>
  57. </footer>
  58. @if (exportError()) { <p class="export-error" role="alert">{{ exportError() }}</p> }
  59. @if (exportMessage()) { <p class="export-message" role="status">{{ exportMessage() }}</p> }
  60. @if (generatedFiles().length) {
  61. <section class="generated-files" aria-label="已生成的 PDF">
  62. <p>如浏览器拦截了多文件下载,请允许下载多个文件,或点击下方文件分别保存。</p>
  63. @for (file of generatedFiles(); track file.url) { <a [href]="file.url" [download]="file.filename">{{ file.label }} PDF</a> }
  64. </section>
  65. }
  66. @if (pdfReports().length) {
  67. <div class="pdf-render" aria-hidden="true"><div #pdfContent><app-lesson-report-pdf [reports]="pdfReports()" [variant]="renderVariant()" /></div></div>
  68. }
  69. </section>