|
@@ -0,0 +1,163 @@
|
|
|
|
+// steps/prepare-step.component.ts
|
|
|
|
+import { Component } from '@angular/core';
|
|
|
|
+import { CommonModule } from '@angular/common';
|
|
|
|
+import { Router } from '@angular/router';
|
|
|
|
+import { Interviewer } from '../../interviewer';
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@Component({
|
|
|
|
+ selector: 'app-prepare-step',
|
|
|
|
+ standalone: true,
|
|
|
|
+ imports: [CommonModule,Interviewer],
|
|
|
|
+ template: `
|
|
|
|
+ <div class="sci-fi-card">
|
|
|
|
+ <h2 class="sci-fi-title">面试准备</h2>
|
|
|
|
+ <p class="sci-fi-subtitle">请确保您的设备满足以下要求</p>
|
|
|
|
+
|
|
|
|
+ <div class="checklist">
|
|
|
|
+ <div class="check-item" [class.checked]="micChecked">
|
|
|
|
+ <div class="check-icon" (click)="toggleMicCheck()">
|
|
|
|
+ <svg viewBox="0 0 24 24">
|
|
|
|
+ <path [attr.d]="micChecked ? 'M9,3A4,4 0 0,1 13,7H5A4,4 0 0,1 9,3M11.84,9.82L11,18H10V19A2,2 0 0,0 12,21A2,2 0 0,0 14,19V14A4,4 0 0,1 18,10H20L19,11L20,10H18A2,2 0 0,0 16,12V19A4,4 0 0,1 12,23A4,4 0 0,1 8,19V18H7L6.16,9.82C5.67,9.32 5.31,8.7 5.13,8H12.87C12.69,8.7 12.33,9.32 11.84,9.82M9,11A1,1 0 0,0 8,12A1,1 0 0,0 9,13A1,1 0 0,0 10,12A1,1 0 0,0 9,11Z' : 'M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z'"
|
|
|
|
+ fill="currentColor" />
|
|
|
|
+ </svg>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="check-content">
|
|
|
|
+ <h3>麦克风检测</h3>
|
|
|
|
+ <p>请允许使用麦克风并测试录音</p>
|
|
|
|
+ <div class="audio-meter" *ngIf="micChecked">
|
|
|
|
+ <div class="level" [style.width.%]="audioLevel"></div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="check-item" [class.checked]="cameraChecked">
|
|
|
|
+ <div class="check-icon" (click)="toggleCameraCheck()">
|
|
|
|
+ <svg viewBox="0 0 24 24">
|
|
|
|
+ <path [attr.d]="cameraChecked ? 'M4,4H7L9,2H15L17,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9Z' : 'M4,4H7L9,2H15L17,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4M12,10.5A1.5,1.5 0 0,1 13.5,12A1.5,1.5 0 0,1 12,13.5A1.5,1.5 0 0,1 10.5,12A1.5,1.5 0 0,1 12,10.5M7,6A1,1 0 0,1 8,7A1,1 0 0,1 7,8A1,1 0 0,1 6,7A1,1 0 0,1 7,6M17,6A1,1 0 0,1 18,7A1,1 0 0,1 17,8A1,1 0 0,1 16,7A1,1 0 0,1 17,6Z'"
|
|
|
|
+ fill="currentColor" />
|
|
|
|
+ </svg>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="check-content">
|
|
|
|
+ <h3>摄像头检测</h3>
|
|
|
|
+ <p>请允许使用摄像头并调整角度</p>
|
|
|
|
+ <div class="camera-preview" *ngIf="cameraChecked">
|
|
|
|
+ <div class="placeholder">
|
|
|
|
+ <svg viewBox="0 0 24 24">
|
|
|
|
+ <path fill="currentColor" d="M12,12A3,3 0 0,0 9,15A3,3 0 0,0 12,18A3,3 0 0,0 15,15A3,3 0 0,0 12,12M12,20A5,5 0 0,1 7,15A5,5 0 0,1 12,10A5,5 0 0,1 17,15A5,5 0 0,1 12,20M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8C10.89,8 10,7.1 10,6C10,4.89 10.89,4 12,4M17,2H7C5.89,2 5,2.89 5,4V20A2,2 0 0,0 7,22H17A2,2 0 0,0 19,20V4C19,2.89 18.1,2 17,2Z" />
|
|
|
|
+ </svg>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="check-item" [class.checked]="networkChecked">
|
|
|
|
+ <div class="check-icon" (click)="toggleNetworkCheck()">
|
|
|
|
+ <svg viewBox="0 0 24 24">
|
|
|
|
+ <path [attr.d]="networkChecked ? 'M12,3C16.97,3 21,7.03 21,12C21,16.97 16.97,21 12,21C7.03,21 3,16.97 3,12C3,7.03 7.03,3 12,3M12,7C9.24,7 7,9.24 7,12C7,14.76 9.24,17 12,17C14.76,17 17,14.76 17,12C17,9.24 14.76,7 12,7Z' : 'M12,3C16.97,3 21,7.03 21,12C21,16.97 16.97,21 12,21C7.03,21 3,16.97 3,12C3,7.03 7.03,3 12,3M12,5C8.14,5 5,8.14 5,12C5,15.86 8.14,19 12,19C15.86,19 19,15.86 19,12C19,8.14 15.86,5 12,5Z'"
|
|
|
|
+ fill="currentColor" />
|
|
|
|
+ </svg>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="check-content">
|
|
|
|
+ <h3>网络检测</h3>
|
|
|
|
+ <p>确保网络连接稳定</p>
|
|
|
|
+ <div class="network-stats" *ngIf="networkChecked">
|
|
|
|
+ <div class="stat">
|
|
|
|
+ <span class="label">延迟</span>
|
|
|
|
+ <span class="value">{{ ping }} ms</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="stat">
|
|
|
|
+ <span class="label">下载</span>
|
|
|
|
+ <span class="value">{{ downloadSpeed }} Mbps</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="stat">
|
|
|
|
+ <span class="label">上传</span>
|
|
|
|
+ <span class="value">{{ uploadSpeed }} Mbps</span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="interview-tips">
|
|
|
|
+ <h3>面试须知</h3>
|
|
|
|
+ <ul>
|
|
|
|
+ <li>请选择一个安静、光线充足的环境</li>
|
|
|
|
+ <li>确保面试期间不会被打扰</li>
|
|
|
|
+ <li>准备好您的简历和相关项目经验</li>
|
|
|
|
+ <li>AI面试官会评估您的技术能力和沟通技巧</li>
|
|
|
|
+ <li>每个问题有固定回答时间,请注意倒计时</li>
|
|
|
|
+ </ul>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <button class="sci-fi-button primary" [disabled]="!allChecked"
|
|
|
|
+ (click)="startInterview()">
|
|
|
|
+ 开始AI面试
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ `,
|
|
|
|
+ styleUrls: ['./prepare-step.scss']
|
|
|
|
+})
|
|
|
|
+export class PrepareStep {
|
|
|
|
+ micChecked = false;
|
|
|
|
+ cameraChecked = false;
|
|
|
|
+ networkChecked = false;
|
|
|
|
+
|
|
|
|
+ audioLevel = 0;
|
|
|
|
+ ping = 0;
|
|
|
|
+ downloadSpeed = 0;
|
|
|
|
+ uploadSpeed = 0;
|
|
|
|
+
|
|
|
|
+ constructor(private router: Router,public interviewer: Interviewer) {}
|
|
|
|
+
|
|
|
|
+ get allChecked(): boolean {
|
|
|
|
+ return this.micChecked && this.cameraChecked && this.networkChecked;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ toggleMicCheck() {
|
|
|
|
+ this.micChecked = !this.micChecked;
|
|
|
|
+ if (this.micChecked) {
|
|
|
|
+ this.simulateAudioTest();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ toggleCameraCheck() {
|
|
|
|
+ this.cameraChecked = !this.cameraChecked;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ toggleNetworkCheck() {
|
|
|
|
+ this.networkChecked = !this.networkChecked;
|
|
|
|
+ if (this.networkChecked) {
|
|
|
|
+ this.simulateNetworkTest();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ simulateAudioTest() {
|
|
|
|
+ let count = 0;
|
|
|
|
+ const interval = setInterval(() => {
|
|
|
|
+ this.audioLevel = Math.min(100, Math.max(0, Math.random() * 120 - 10));
|
|
|
|
+ count++;
|
|
|
|
+
|
|
|
|
+ if (count >= 20) {
|
|
|
|
+ clearInterval(interval);
|
|
|
|
+ this.audioLevel = 80 + Math.random() * 20;
|
|
|
|
+ }
|
|
|
|
+ }, 100);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ simulateNetworkTest() {
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.ping = Math.floor(20 + Math.random() * 50);
|
|
|
|
+ this.downloadSpeed = parseFloat((50 + Math.random() * 50).toFixed(1));
|
|
|
|
+ this.uploadSpeed = parseFloat((10 + Math.random() * 20).toFixed(1));
|
|
|
|
+ }, 1000);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ startInterview() {
|
|
|
|
+ if (this.allChecked) {
|
|
|
|
+ this.router.navigate(['/auth/home/interviewer/interview']);
|
|
|
|
+ this.interviewer.currentStep=2;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|