Browse Source

feat: reinterview

0235702 1 day ago
parent
commit
b66bbf613b

+ 60 - 2
interview-web/package-lock.json

@@ -18,7 +18,9 @@
         "@fortawesome/fontawesome-free": "^6.7.2",
         "@fortawesome/fontawesome-free": "^6.7.2",
         "@fortawesome/fontawesome-svg-core": "^6.7.2",
         "@fortawesome/fontawesome-svg-core": "^6.7.2",
         "@fortawesome/free-solid-svg-icons": "^6.7.2",
         "@fortawesome/free-solid-svg-icons": "^6.7.2",
+        "chart.js": "^4.5.0",
         "echarts": "^5.6.0",
         "echarts": "^5.6.0",
+        "ng2-charts": "^8.0.0",
         "rxjs": "~7.8.0",
         "rxjs": "~7.8.0",
         "swiper": "^11.2.10",
         "swiper": "^11.2.10",
         "tslib": "^2.3.0",
         "tslib": "^2.3.0",
@@ -214,6 +216,22 @@
         }
         }
       }
       }
     },
     },
+    "node_modules/@angular/cdk": {
+      "version": "20.0.5",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.0.5.tgz",
+      "integrity": "sha512-WhJ1I/ib/Za0qjWkSzMYV0gM8NOWrtOcZ2TYZ4aYFsjd8E13rGhxOez0DWt2sN3vfjAc1iWMmGGbNZrkp98adg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "parse5": "^7.1.2",
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "^20.0.0 || ^21.0.0",
+        "@angular/core": "^20.0.0 || ^21.0.0",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
     "node_modules/@angular/cli": {
     "node_modules/@angular/cli": {
       "version": "20.0.4",
       "version": "20.0.4",
       "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.0.4.tgz",
       "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.0.4.tgz",
@@ -1714,6 +1732,12 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
       }
     },
     },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
+      "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+      "license": "MIT"
+    },
     "node_modules/@listr2/prompt-adapter-inquirer": {
     "node_modules/@listr2/prompt-adapter-inquirer": {
       "version": "2.0.22",
       "version": "2.0.22",
       "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.22.tgz",
       "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.22.tgz",
@@ -3872,6 +3896,18 @@
       "dev": true,
       "dev": true,
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/chart.js": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
+      "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=8"
+      }
+    },
     "node_modules/chokidar": {
     "node_modules/chokidar": {
       "version": "4.0.3",
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
@@ -6089,6 +6125,12 @@
       "dev": true,
       "dev": true,
       "license": "MIT"
       "license": "MIT"
     },
     },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
     "node_modules/log-symbols": {
     "node_modules/log-symbols": {
       "version": "6.0.0",
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
@@ -6659,6 +6701,24 @@
         "node": ">= 0.6"
         "node": ">= 0.6"
       }
       }
     },
     },
+    "node_modules/ng2-charts": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-8.0.0.tgz",
+      "integrity": "sha512-nofsNHI2Zt+EAwT+BJBVg0kgOhNo9ukO4CxULlaIi7VwZSr7I1km38kWSoU41Oq6os6qqIh5srnL+CcV+RFPFA==",
+      "license": "MIT",
+      "dependencies": {
+        "lodash-es": "^4.17.15",
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@angular/cdk": ">=19.0.0",
+        "@angular/common": ">=19.0.0",
+        "@angular/core": ">=19.0.0",
+        "@angular/platform-browser": ">=19.0.0",
+        "chart.js": "^3.4.0 || ^4.0.0",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
     "node_modules/node-addon-api": {
     "node_modules/node-addon-api": {
       "version": "6.1.0",
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
@@ -7095,7 +7155,6 @@
       "version": "7.3.0",
       "version": "7.3.0",
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
       "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
       "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
-      "dev": true,
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "entities": "^6.0.0"
         "entities": "^6.0.0"
@@ -7149,7 +7208,6 @@
       "version": "6.0.1",
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
       "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
       "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
       "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "license": "BSD-2-Clause",
       "engines": {
       "engines": {
         "node": ">=0.12"
         "node": ">=0.12"

+ 2 - 0
interview-web/package.json

@@ -30,7 +30,9 @@
     "@fortawesome/fontawesome-free": "^6.7.2",
     "@fortawesome/fontawesome-free": "^6.7.2",
     "@fortawesome/fontawesome-svg-core": "^6.7.2",
     "@fortawesome/fontawesome-svg-core": "^6.7.2",
     "@fortawesome/free-solid-svg-icons": "^6.7.2",
     "@fortawesome/free-solid-svg-icons": "^6.7.2",
+    "chart.js": "^4.5.0",
     "echarts": "^5.6.0",
     "echarts": "^5.6.0",
+    "ng2-charts": "^8.0.0",
     "rxjs": "~7.8.0",
     "rxjs": "~7.8.0",
     "swiper": "^11.2.10",
     "swiper": "^11.2.10",
     "tslib": "^2.3.0",
     "tslib": "^2.3.0",

+ 7 - 29
interview-web/src/lib/ai-api.service.ts

@@ -1,11 +1,6 @@
 import { Injectable } from '@angular/core';
 import { Injectable } from '@angular/core';
 import { TestCompletion, generateInterviewEvaluation } from './completion';
 import { TestCompletion, generateInterviewEvaluation } from './completion';
 
 
-export interface TestMessage {
-    role: string;
-    content: string;
-}
-
 @Injectable({
 @Injectable({
     providedIn: 'root'
     providedIn: 'root'
 })
 })
@@ -33,10 +28,14 @@ export class AiApiService {
                 onMessage
                 onMessage
             );
             );
             
             
-            // 确保返回标准化的评估结果
+            // 增强相关性检查
+            if (question.includes("介绍") && evaluation.metrics.relevance < 40) {
+                evaluation.metrics.relevance = 40; // 自我介绍最低保证40分相关性
+            }
+            
             return {
             return {
                 score: evaluation.score || 0,
                 score: evaluation.score || 0,
-                feedback: evaluation.feedback || "暂无反馈",
+                feedback: evaluation.feedback || "回答基本符合要求",
                 followUpQuestion: evaluation.followUpQuestion || "能否详细说明一下?",
                 followUpQuestion: evaluation.followUpQuestion || "能否详细说明一下?",
                 metrics: {
                 metrics: {
                     expressiveness: evaluation.metrics?.expressiveness || 0,
                     expressiveness: evaluation.metrics?.expressiveness || 0,
@@ -62,25 +61,4 @@ export class AiApiService {
             }
             }
         };
         };
     }
     }
-
-    async generateFollowUpQuestion(
-        conversationHistory: TestMessage[],
-        onMessage?: (content: string) => void
-    ): Promise<string> {
-        const prompt = `根据以下对话历史,生成一个有针对性的追问问题:
-${conversationHistory.map(msg => `${msg.role}: ${msg.content}`).join('\n')}
-
-要求:
-- 问题应针对候选人的回答深入挖掘
-- 问题应简洁明了
-- 只返回问题内容,不要包含其他说明`;
-
-        const messages: TestMessage[] = [
-            { role: "user", content: prompt }
-        ];
-
-        const completion = new TestCompletion(messages);
-        const result = await completion.sendMessage(messages, onMessage);
-        return result || "能否详细说明一下?";
-    }
-}  
+}

+ 3 - 0
interview-web/src/lib/completion.ts

@@ -8,7 +8,10 @@ export class TestCompletion {
     token: string = "r:60abef69e7cd8181b146ceaba1fdbf02";
     token: string = "r:60abef69e7cd8181b146ceaba1fdbf02";
     messageList: TestMessage[] = [];
     messageList: TestMessage[] = [];
     stream: boolean = true;
     stream: boolean = true;
+    interviewEnded = false;
+    recognition: any;
 
 
+    
     constructor(messageList?: TestMessage[]) {
     constructor(messageList?: TestMessage[]) {
         this.messageList = messageList || this.messageList;
         this.messageList = messageList || this.messageList;
     }
     }

+ 113 - 0
interview-web/src/modules/interview/mobile/page-interview/page-interview.scss

@@ -437,3 +437,116 @@ body {
   margin-right: 8px;
   margin-right: 8px;
   vertical-align: middle;
   vertical-align: middle;
 }
 }
+.interview-ended {
+  text-align: center;
+  padding: 20px;
+  background: white;
+  border-radius: 15px;
+  box-shadow: 0 5px 15px rgba(0,0,0,0.05);
+  
+  h3 {
+    color: var(--primary-blue);
+    margin-bottom: 15px;
+  }
+}
+
+.restart-btn {
+  background: linear-gradient(135deg, var(--primary-blue), var(--accent-orange));
+  color: white;
+  border: none;
+  padding: 12px 24px;
+  border-radius: 30px;
+  font-size: 16px;
+  font-weight: 500;
+  cursor: pointer;
+  box-shadow: 0 4px 12px rgba(42, 92, 170, 0.3);
+  transition: all 0.3s ease;
+  display: inline-flex;
+  align-items: center;
+  gap: 8px;
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 6px 16px rgba(42, 92, 170, 0.4);
+  }
+  
+  i {
+    margin-right: 8px;
+  }
+}
+
+/* 语音识别临时结果样式 */
+.message-user .message-bubble {
+  background: var(--primary-blue);
+  color: white;
+  border-top-right-radius: 5px;
+  margin-left: auto;
+}
+
+.message-user .message-meta {
+  justify-content: flex-end;
+}
+/* 新增用户消息样式 */
+.message-user {
+  align-items: flex-end;
+  margin-left: auto;
+  max-width: 80%;
+
+  .user-bubble {
+    background: #2A5CAA;
+    color: white;
+    border-top-right-radius: 5px;
+    position: relative;
+    padding-right: 40px;
+  }
+
+  .user-meta {
+    justify-content: flex-end;
+    color: #718096;
+  }
+}
+
+/* 语音输入指示器 */
+.recording-indicator {
+  position: absolute;
+  right: 12px;
+  bottom: 12px;
+  display: flex;
+  gap: 4px;
+
+  .dot {
+    display: inline-block;
+    width: 8px;
+    height: 8px;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.6);
+    animation: pulse 1.4s infinite ease-in-out;
+
+    &:nth-child(2) {
+      animation-delay: 0.2s;
+    }
+    &:nth-child(3) {
+      animation-delay: 0.4s;
+    }
+  }
+}
+
+@keyframes pulse {
+  0%, 100% { transform: scale(0.8); opacity: 0.5; }
+  50% { transform: scale(1.1); opacity: 1; }
+}
+
+/* AI消息样式保持原样 */
+.message-ai {
+  align-items: flex-start;
+  
+  .ai-bubble {
+    background: #EDF2F7;
+    color: #2D3748;
+    border-top-left-radius: 5px;
+  }
+  
+  .ai-meta {
+    justify-content: flex-start;
+  }
+}

+ 5 - 0
interview-web/src/modules/interview/mobile/page-job-hunting/page-job-hunting.scss

@@ -186,4 +186,9 @@ header {
   .filter-group {
   .filter-group {
     width: 100%;
     width: 100%;
   }
   }
+}
+.timer {
+  font-size: 1.1rem;  /* 稍微增大字体 */
+  min-width: 80px;    /* 确保有足够空间显示4位数 */
+  text-align: right;
 }
 }