import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import * as echarts from 'echarts'; @Component({ selector: 'app-page-interview', standalone: true, imports: [CommonModule], templateUrl: './page-interview.html', styleUrl: './page-interview.scss' }) export class PageInterview implements OnInit, AfterViewInit { remainingTime = '04:32'; isAnalyzing = false; showSubmitButton = false; userAnswer = ''; // 在组件中定义Base64编码的SVG avatarImages = { default: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzNiAzNiI+PHBhdGggZmlsbD0iI0ZGQjkwMCIgZD0iTTM2IDE4YzAgOS45NDEtOC4wNTkgMTgtMTggMThTMCAyNy45NDEgMCAxOCA4LjA1OSAwIDE4IDBzMTggOC4wNTkgMTggMTgiLz48cGF0aCBmaWxsPSIjNjYyMjEyIiBkPSJNMTguODQ1IDEzLjE0OGMuMTM0LS4yNDYuMzU2LS4zNjkuNjU3LS4zNjkuMzA5IDAgLjUyOS4xMjMuNjY0LjM2OS4xMzYuMjQ1LjIwNC41NzkuMjA0IDEuMDAzIDAgLjQxNS0uMDY4Ljc0LS4yMDQuOTg1LS4xMzUuMjQ0LS4zNTUuMzY2LS42NjQuMzY2LS4zMDEgMC0uNTIzLS4xMjItLjY1Ny0uMzY2LS4xMzUtLjI0NS0uMjAyLS41Ny0uMjAyLS45ODUgMC0uNDI0LjA2Ny0uNzU4LjIwMi0xLjAwM3ptLTUuNDQ4IDBjLjEzNS0uMjQ2LjM1Ni0uMzY5LjY1OC0uMzY5LjMwOSAwIC41MjkuMTIzLjY2NC4zNjkuMTM2LjI0NS4yMDQuNTc5LjIwNCAxLjAwMyAwIC40MTUtLjA2OC43NC0uMjA0Ljk4NS0uMTM1LjI0NC0uMzU1LjM2Ni0uNjY0LjM2Ni0uMzAyIDAtLjUyMy0uMTIyLS42NTgtLjM2Ni0uMTM0LS4yNDUtLjIwMS0uNTctLjIwMS0uOTg1IDAtLjQyNC4wNjctLjc1OC4yMDEtMS4wMDN6Ii8+PHBhdGggZmlsbD0iIzY2MjIxMiIgZD0iTTE4IDIxLjUzYy0yLjc2NCAwLTQuOTUxLS40MTktNC45NTEtMS4wNTQgMC0uNjM1IDIuMTg3LTEuMDU0IDQuOTUxLTEuMDU0IDIuNzYzIDAgNC45NTEuNDE5IDQuOTUxIDEuMDU0IDAgLjYzNS0yLjE4OCAxLjA1NC00Ljk1MSAxLjA1NHptMC0yLjEwOGMtMy4wMTcgMC01LjQ1MS0uNDk0LTUuNDUxLTEuMDU0czIuNDM0LTEuMDU0IDUuNDUxLTEuMDU0YzMuMDE4IDAgNS40NTEuNDk0IDUuNDUxIDEuMDU0cy0yLjQzMyAxLjA1NC01LjQ1MSAxLjA1NHoiLz48L3N2Zz4=', speaking: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzNiAzNiI+PHBhdGggZmlsbD0iI0ZGQjkwMCIgZD0iTTM2IDE4YzAgOS45NDEtOC4wNTkgMTgtMTggMThTMCAyNy45NDEgMCAxOCA4LjA1OSAwIDE4IDBzMTggOC4wNTkgMTggMTgiLz48cGF0aCBmaWxsPSIjNjYyMjEyIiBkPSJNMTggMjEuNTNjLTIuNzY0IDAtNC45NTEtLjQxOS00Ljk1MS0xLjA1NCAwLS42MzUgMi4xODctMS4wNTQgNC45NTEtMS4wNTQgMi43NjMgMCA0Ljk1MS40MTkgNC45NTEgMS4wNTQgMCAuNjM1LTIuMTg4IDEuMDU0LTQuOTUxIDEuMDU0em0wLTIuMTA4Yy0zLjAxNyAwLTUuNDUxLS40OTQtNS40NTEtMS4wNTRzMi40MzQtMS4wNTQgNS40NTEtMS4wNTRjMy4wMTggMCA1LjQ1MS40OTQgNS40NTEgMS4wNTRzLTIuNDMzIDEuMDU0LTUuNDUxIDEuMDU0eiIvPjwvc3ZnPg==', // 其他表情... }; // 问题相关 questions = [ "欢迎参加本次AI面试,我是您的面试官AI助手。", "首先,请简单介绍一下您自己。", "您在过去工作中遇到的最大技术挑战是什么?您是如何解决的?", "请描述一个您与团队意见不合时,您是如何处理的案例。" ]; currentQuestionIndex = 0; currentQuestion = this.questions[0]; showQuestionCard = false; progress = 30; // 对话相关 messages: {sender: 'ai' | 'user', text: string}[] = []; // 语音相关 isListening = false; isSpeaking = false; // 头像和表情相关 avatarImage = "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/1f913.svg"; expressionText = "正在聆听您的回答..."; // 仪表盘相关 showRadarChart = false; metrics = { expressiveness: 82, professionalism: 65, relevance: 73 }; // 预设回答 presetAnswers = [ "我是张伟,有5年Java开发经验,擅长分布式系统设计。", "我们曾遇到高并发下的数据库瓶颈,我通过引入Redis缓存和分库分表解决了问题。", "有一次在技术方案选择上,我通过数据对比和原型验证说服了团队采用我的方案。" ]; @ViewChild('dialogContainer') dialogContainer!: ElementRef; private radarChart: any; ngOnInit(): void { this.initConversation(); this.startProgressTimer(); } ngAfterViewInit(): void { // 初始化雷达图 this.initRadarChart(); } // 初始化对话 initConversation(): void { this.addAIMessage(this.questions[0]); setTimeout(() => { this.askQuestion(1); }, 1500); } // 提问问题 askQuestion(index: number): void { if (index >= this.questions.length) return; this.currentQuestionIndex = index; this.currentQuestion = this.questions[index]; // 添加到对话框 this.addAIMessage(this.currentQuestion); // 更新头像状态 this.avatarImage = "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/1f4ac.svg"; this.expressionText = "等待您的回答..."; } // 添加AI消息 addAIMessage(text: string): void { this.messages.push({ sender: 'ai', text: text }); // 滚动到底部 setTimeout(() => { this.dialogContainer.nativeElement.scrollTop = this.dialogContainer.nativeElement.scrollHeight; }, 0); // 自动语音播报 this.speakText(text); } // 添加用户消息 addUserMessage(text: string): void { this.messages.push({ sender: 'user', text: text }); // 滚动到底部 setTimeout(() => { this.dialogContainer.nativeElement.scrollTop = this.dialogContainer.nativeElement.scrollHeight; }, 0); } // 语音播报 speakText(text: string): void { if (this.isSpeaking) return; this.isSpeaking = true; this.avatarImage = "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/1f5e3.svg"; this.expressionText = "正在播报问题..."; // 使用Web Speech API const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'zh-CN'; utterance.rate = 0.9; utterance.pitch = 1; utterance.onend = () => { this.isSpeaking = false; this.avatarImage = "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/1f4ac.svg"; this.expressionText = "等待您的回答..."; }; speechSynthesis.speak(utterance); } // 开始语音输入 startVoiceInput(): void { this.isListening = true; this.expressionText = "正在聆听您的回答..."; console.log('语音输入开始'); } // 停止语音输入 stopVoiceInput(): void { this.isListening = false; // 模拟语音识别结果 this.userAnswer = this.presetAnswers[this.currentQuestionIndex - 1] || "这是我的回答..."; this.showSubmitButton = true; // 显示提交按钮 // 不再自动提交,等待用户点击 this.avatarImage = this.avatarImages.default; this.expressionText = "请确认您的回答"; } submitAnswer(): void { this.showSubmitButton = false; this.addUserMessage(this.userAnswer); // 开始分析 this.isAnalyzing = true; this.avatarImage = this.avatarImages.default; this.expressionText = "正在分析您的回答..."; // 模拟分析过程 (2秒) setTimeout(() => { this.isAnalyzing = false; // 问下一题或结束 if (this.currentQuestionIndex < this.questions.length - 1) { this.askQuestion(this.currentQuestionIndex + 1); } else { this.addAIMessage("感谢您的回答,面试即将结束,正在生成最终报告..."); this.avatarImage = this.avatarImages.speaking; this.expressionText = "生成最终评估中..."; } }, 2000); } // 播放问题 playQuestion(): void { if (!this.isSpeaking) { this.speakText(this.questions[this.currentQuestionIndex]); } } // 切换雷达图 toggleRadarChart(): void { this.showRadarChart = !this.showRadarChart; if (this.showRadarChart) { setTimeout(() => { this.initRadarChart(); }, 0); } } // 初始化雷达图 initRadarChart(): void { if (!this.showRadarChart) return; const chartDom = document.getElementById('radarChart'); if (!chartDom) return; if (this.radarChart) { this.radarChart.dispose(); } this.radarChart = echarts.init(chartDom); const option = { backgroundColor: 'transparent', tooltip: {}, radar: { shape: 'circle', indicator: [ { name: '表达能力', max: 100 }, { name: '专业深度', max: 100 }, { name: '岗位匹配', max: 100 }, { name: '应变能力', max: 100 }, { name: '沟通技巧', max: 100 }, { name: '知识广度', max: 100 } ], radius: '65%', axisName: { color: '#4A5568' }, splitArea: { areaStyle: { color: ['rgba(42, 92, 170, 0.1)'] } }, axisLine: { lineStyle: { color: 'rgba(42, 92, 170, 0.3)' } }, splitLine: { lineStyle: { color: 'rgba(42, 92, 170, 0.3)' } } }, series: [{ type: 'radar', data: [ { value: [82, 65, 73, 68, 75, 60], name: '当前表现', areaStyle: { color: 'rgba(42, 92, 170, 0.4)' }, lineStyle: { width: 2, color: 'rgba(42, 92, 170, 0.8)' }, itemStyle: { color: '#2A5CAA' } }, { value: [70, 80, 85, 75, 65, 70], name: '岗位要求', areaStyle: { color: 'rgba(255, 107, 53, 0.2)' }, lineStyle: { width: 2, color: 'rgba(255, 107, 53, 0.8)' }, itemStyle: { color: '#FF6B35' } } ] }] }; this.radarChart.setOption(option); // 响应式调整 window.addEventListener('resize', () => { this.radarChart?.resize(); }); } // 进度条计时器 startProgressTimer(): void { setInterval(() => { this.progress = Math.min(this.progress + 10, 100); }, 5000); } }