page-interview.html 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <div class="interview-container">
  2. <!-- 头部区域 -->
  3. <div class="interview-header">
  4. <div class="logo">AI面试官</div>
  5. <div class="timer">
  6. <i class="fas fa-clock"></i> 剩余时间: {{ remainingTime }}
  7. </div>
  8. </div>
  9. <!-- 数字人区域 -->
  10. <div class="avatar-section">
  11. <div class="avatar-container">
  12. <img [src]="avatarImage" alt="AI头像" class="avatar-img"
  13. (error)="avatarImage = 'assets/default-avatar.svg'">
  14. <div class="voice-wave" [style.animation]="isListening ? 'wave 2s infinite' : 'none'"></div>
  15. </div>
  16. <div class="avatar-expression">
  17. <span class="expression-dot"></span>
  18. <span>{{ expressionText }}</span>
  19. </div>
  20. </div>
  21. <!-- 修改后的对话区域 -->
  22. <div class="dialog-container" #dialogContainer>
  23. @for (message of messages; track $index) {
  24. <div class="message" [ngClass]="{'message-ai': message.sender === 'ai', 'message-user': message.sender === 'user'}">
  25. <div class="message-bubble" [ngClass]="{'ai-bubble': message.sender === 'ai', 'user-bubble': message.sender === 'user'}">
  26. {{ message.text }}
  27. </div>
  28. <div class="message-meta" [ngClass]="{'ai-meta': message.sender === 'ai', 'user-meta': message.sender === 'user'}">
  29. <i [class]="message.sender === 'ai' ? 'fas fa-robot' : 'fas fa-user'"></i>
  30. {{ message.sender === 'ai' ? 'AI面试官' : '您' }}
  31. </div>
  32. </div>
  33. }
  34. <!-- 实时语音转文字显示 -->
  35. @if (isListening && interimTranscript) {
  36. <div class="message message-user">
  37. <div class="message-bubble user-bubble">
  38. {{ interimTranscript }}
  39. <div class="recording-indicator">
  40. <span class="dot"></span>
  41. <span class="dot"></span>
  42. <span class="dot"></span>
  43. </div>
  44. </div>
  45. <div class="message-meta user-meta">
  46. <i class="fas fa-user"></i> 您 (正在说话)
  47. </div>
  48. </div>
  49. }
  50. </div>
  51. <!-- 问题卡片 -->
  52. @if (showQuestionCard && !interviewEnded) {
  53. <div class="question-card">
  54. <div class="question-text">
  55. {{ currentQuestion }}
  56. </div>
  57. <div class="question-progress">
  58. <span>问题 {{ currentQuestionIndex + 1 }}/{{ mainQuestions.length }}</span>
  59. <div class="progress-bar">
  60. <div class="progress-fill" [style.width]="progress + '%'"></div>
  61. </div>
  62. <span>{{ progress }}%</span>
  63. </div>
  64. </div>
  65. }
  66. <!-- 语音输入区域 -->
  67. <div class="voice-input-section">
  68. @if (!interviewEnded) {
  69. <div class="voice-controls">
  70. <!-- 麦克风按钮 -->
  71. <button class="voice-btn"
  72. (touchstart)="startVoiceInput()" (touchend)="stopVoiceInput()"
  73. (mousedown)="startVoiceInput()" (mouseup)="stopVoiceInput()"
  74. (mouseleave)="stopVoiceInput()"
  75. [class.active]="isListening">
  76. <i class="fas fa-microphone"></i>
  77. <div class="voice-wave"></div>
  78. <span class="press-hint">长按说话</span>
  79. </button>
  80. <!-- 播放问题按钮 -->
  81. <button class="voice-btn"
  82. (click)="playQuestion()"
  83. [disabled]="isListening || isAnalyzing"
  84. style="background: linear-gradient(135deg, #48BB78, #38A169);">
  85. <i class="fas fa-play"></i>
  86. </button>
  87. </div>
  88. <!-- 确认/取消按钮 -->
  89. @if (showSubmitButton || showCancelButton) {
  90. <div class="answer-confirm-buttons">
  91. <button class="submit-btn" (click)="submitAnswer()" [disabled]="isAnalyzing">
  92. <svg class="check-icon" viewBox="0 0 24 24" width="16" height="16">
  93. <path fill="currentColor" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" style="color: burlywood;"/>
  94. </svg>
  95. <div style="color: black;">确认回答</div>
  96. </button>
  97. <button class="cancel-btn" (click)="cancelAnswer()" [disabled]="isAnalyzing">
  98. <i class="fas fa-times"></i>
  99. 取消回答
  100. </button>
  101. </div>
  102. }
  103. <!-- 操作提示 -->
  104. <div class="voice-hint">
  105. @if (isListening) {
  106. 正在聆听,请回答问题...
  107. } @else if (isSpeaking) {
  108. 正在播报问题...
  109. } @else if (isAnalyzing) {
  110. 正在分析您的回答...
  111. } @else {
  112. 按住麦克风按钮开始回答,点击播放按钮重复听取问题
  113. }
  114. </div>
  115. } @else {
  116. <!-- 面试结束状态 -->
  117. <div class="interview-ended">
  118. <h3>面试已结束</h3>
  119. <p>感谢您参与本次AI面试</p>
  120. <button class="restart-btn" (click)="restartInterview()">
  121. <i class="fas fa-redo"></i> 重新开始面试
  122. </button>
  123. </div>
  124. }
  125. </div>
  126. @if (isListening && silenceDuration > 0) {
  127. <div class="silence-timer">
  128. 思考时间: {{ MAX_SILENCE - silenceDuration }}秒
  129. </div>
  130. }
  131. <!-- 分析仪表盘 -->
  132. @if (!interviewEnded) {
  133. <div class="dashboard-section">
  134. <div class="dashboard-title">
  135. <h3>实时分析面板</h3>
  136. <button class="toggle-btn" (click)="toggleRadarChart()">
  137. <i [class]="showRadarChart ? 'fas fa-chart-bar' : 'fas fa-chart-radar'"></i>
  138. {{ showRadarChart ? '隐藏雷达图' : '显示雷达图' }}
  139. </button>
  140. </div>
  141. <div class="dashboard-content">
  142. <div class="metric-item expressiveness">
  143. <div class="metric-header">
  144. <span class="metric-name">表达能力</span>
  145. <span class="metric-value">{{ metrics.expressiveness }}/100</span>
  146. </div>
  147. <div class="metric-bar">
  148. <div class="metric-fill" [style.width]="metrics.expressiveness + '%'"></div>
  149. </div>
  150. </div>
  151. <div class="metric-item professionalism">
  152. <div class="metric-header">
  153. <span class="metric-name">专业度</span>
  154. <span class="metric-value">{{ metrics.professionalism }}/100</span>
  155. </div>
  156. <div class="metric-bar">
  157. <div class="metric-fill" [style.width]="metrics.professionalism + '%'"></div>
  158. </div>
  159. </div>
  160. <div class="metric-item relevance">
  161. <div class="metric-header">
  162. <span class="metric-name">岗位匹配度</span>
  163. <span class="metric-value">{{ metrics.relevance }}/100</span>
  164. </div>
  165. <div class="metric-bar">
  166. <div class="metric-fill" [style.width]="metrics.relevance + '%'"></div>
  167. </div>
  168. </div>
  169. </div>
  170. <!-- 雷达图容器 -->
  171. @if (showRadarChart) {
  172. <div id="radarChart" class="radar-chart"></div>
  173. }
  174. </div>
  175. }
  176. </div>