yangg 1 week ago
parent
commit
f261e286e5

+ 1 - 1
.env.development

@@ -6,7 +6,7 @@ ENV = 'development'
 # 本地环境接口地址 121.36.251.245
 
 
-VITE_API_URL = 'https://backend.qicai321.com'
+VITE_API_URL = 'http://192.168.100.187:8083'
 # VITE_API_URL = 'https://backend.qicai321.com'
 VITE_API_WX_URL='https://api.weixin.qq.com/'
 VITE_API_URL_MINLONG='http://117.185.80.170:7861'

+ 182 - 113
src/views/JobApplication/report/report.vue

@@ -79,14 +79,106 @@
         :before-close="handleEyeEmotionDialogClose"
       >
         <div class="eye-emotion-content">
-          <!-- 这里添加眼动情绪检测的内容 -->
           <div class="emotion-data">
-            <h3>眼动追踪数据</h3>
-            <!-- 这里可以添加图表或详细数据 -->
+            <h3>情绪分析概览</h3>
+            <el-descriptions :column="2" border>
+              <el-descriptions-item label="主要情绪">
+                <el-tag :type="emotionAnalysis.emotion_score <= 3 ? 'danger' : emotionAnalysis.emotion_score <= 6 ? 'warning' : 'success'">
+                  {{ emotionAnalysis.primary_emotion || '暂无数据' }}
+                </el-tag>
+              </el-descriptions-item>
+             <!--  <el-descriptions-item label="情绪评分">
+                <span :style="{ color: emotionAnalysis.emotion_score <= 3 ? '#F56C6C' : emotionAnalysis.emotion_score <= 6 ? '#E6A23C' : '#67C23A' }">
+                  {{ emotionAnalysis.emotion_score || '暂无数据' }}/10
+                </span>
+              </el-descriptions-item> -->
+              <el-descriptions-item label="情绪稳定性">
+                {{ emotionAnalysis.emotion_stability || '暂无数据' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="置信度">
+                {{ emotionAnalysis.confidence_level || '暂无数据' }}
+              </el-descriptions-item>
+            </el-descriptions>
           </div>
-          <div class="emotion-analysis">
-            <h3>情绪分析结果</h3>
-            <!-- 这里可以添加分析结果 -->
+
+          <div class="emotion-analysis mt-4">
+            <h3>详细分析</h3>
+            <el-collapse v-if="emotionAnalysis.emotion_details">
+              <el-collapse-item title="情绪指标" name="1">
+                <template #default>
+                  <div v-if="emotionAnalysis.emotion_details.positive_indicators?.length">
+                    <div class="mb-2">
+                      <el-tag type="success" effect="plain">积极表现</el-tag>
+                      <div class="mt-1">
+                        <el-tag
+                          v-for="(item, index) in emotionAnalysis.emotion_details.positive_indicators"
+                          :key="index"
+                          class="mr-2 mb-2"
+                          type="success"
+                          effect="light"
+                        >
+                          {{ item }}
+                        </el-tag>
+                      </div>
+                    </div>
+                  </div>
+                  <div v-if="emotionAnalysis.emotion_details.negative_indicators?.length">
+                    <div class="mb-2">
+                      <el-tag type="danger" effect="plain">消极表现</el-tag>
+                      <div class="mt-1">
+                        <el-tag
+                          v-for="(item, index) in emotionAnalysis.emotion_details.negative_indicators"
+                          :key="index"
+                          class="mr-2 mb-2"
+                          type="danger"
+                          effect="light"
+                        >
+                          {{ item }}
+                        </el-tag>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="mt-2">
+                    <p><strong>语言流畅度:</strong>{{ emotionAnalysis.emotion_details.language_fluency }}</p>
+                  </div>
+                </template>
+              </el-collapse-item>
+
+              <el-collapse-item title="岗位适配性评估" name="2">
+                <template #default>
+                  <div v-if="emotionAnalysis.job_suitability">
+                    <p><strong>沟通能力:</strong>{{ emotionAnalysis.job_suitability.communication_readiness }}</p>
+                    <p><strong>抗压能力:</strong>{{ emotionAnalysis.job_suitability.pressure_resistance }}</p>
+                  </div>
+                </template>
+              </el-collapse-item>
+
+              <el-collapse-item title="改进建议" name="3">
+                <template #default>
+                  <div v-if="emotionAnalysis.recommendations?.length">
+                    <el-timeline>
+                      <el-timeline-item
+                        v-for="(recommendation, index) in emotionAnalysis.recommendations"
+                        :key="index"
+                        :type="index % 2 === 0 ? 'primary' : 'success'"
+                      >
+                        {{ recommendation }}
+                      </el-timeline-item>
+                    </el-timeline>
+                  </div>
+                </template>
+              </el-collapse-item>
+            </el-collapse>
+
+            <div v-if="emotionAnalysis.summary" class="mt-4">
+              <h4>总结评价</h4>
+              <el-alert
+                :title="emotionAnalysis.summary"
+                :type="emotionAnalysis.emotion_score <= 3 ? 'error' : emotionAnalysis.emotion_score <= 6 ? 'warning' : 'success'"
+                :closable="false"
+                class="mt-2"
+              />
+            </div>
           </div>
         </div>
       </el-dialog>
@@ -276,9 +368,9 @@
       <div ref="cheat" class="detection-section">
         <div class="detection-section-header">
             <div class="section-title">作弊检测</div>
-            <div class="status-tag normal" v-if="true">无异常</div>
+            <div class="status-tag normal" v-if="!abnormalPhotos?.photos_by_type?.identity_changed?.length">无异常</div>
             <el-button 
-              v-if="!true"
+              v-if="abnormalPhotos?.photos_by_type?.identity_changed?.length > 0"
               type="primary" 
               link
               @click="showCheatVerification = true"
@@ -288,11 +380,21 @@
         </div>
      <div class="detection-section-content">
         <div class="detection-items">
-            <div class="item">
-            <span class="status normal">无风险</span>
+          <div class="item">
+            <span class="status" :class="{ 
+                'normal': !abnormalPhotos?.photos_by_type?.identity_changed?.length,
+                'warning': abnormalPhotos?.photos_by_type?.identity_changed?.length > 0 
+            }">
+                {{ !abnormalPhotos?.photos_by_type?.identity_changed?.length ? '无风险' : '存在风险' }}
+            </span>
             <span class="separator">/</span>
-            <span class="desc">转移、替考:检测无异常</span>
-            </div>
+            <span class="desc">
+                异常图片:{{ !abnormalPhotos?.photos_by_type?.identity_changed?.length ? 
+                    '检测无异常' : 
+                    `检测到${abnormalPhotos.photos_by_type.identity_changed.length}处异常` 
+                }}
+            </span>
+        </div>
             <div class="item">
             <span class="status normal">无风险</span>
             <span class="separator">/</span>
@@ -873,7 +975,7 @@
 <el-dialog
   title="作弊检测详情"
   v-model="showCheatVerification"
-  width="80%"
+  width="60%"
   :before-close="handleCheatVerificationClose"
   class="cheat-verification-dialog"
 >
@@ -882,20 +984,19 @@
       <!-- 转移、替考检测 -->
       <div class="verification-item">
         <div class="item-header">
-          <span class="item-title">转移、替考检测</span>
-          <span class="item-status normal">无风险</span>
+          <span class="item-title">异常图片</span>
+          <!-- <span class="item-status normal">无风险</span> -->
         </div>
         <div class="item-details">
-          <p>通过AI人脸识别技术,对考生面部特征进行实时监控,未发现替考或转移行为。</p>
-          <div class="detail-images" v-if="false">
-            <!-- 如果有异常图片证据,这里可以展示 -->
-            <img src="" alt="异常证据" />
-          </div>
+          <p>通过AI人脸识别技术,对考生面部特征进行实时监控,发现异常情况。</p>
+            <div class="detail-images" >
+              <img v-for="photo in abnormalPhotos.photos_by_type.identity_changed" :key="photo.id" :src="'data:image/jpeg;base64,' + photo.photo_data" alt="身份识别图片" />
+            </div>
         </div>
       </div>
 
       <!-- 读稿检测 -->
-      <div class="verification-item">
+     <!--  <div class="verification-item">
         <div class="item-header">
           <span class="item-title">读稿检测</span>
           <span class="item-status normal">无风险</span>
@@ -903,14 +1004,13 @@
         <div class="item-details">
           <p>通过眼动追踪和头部姿态分析,未发现考生有明显读稿行为。</p>
           <div class="detail-images" v-if="false">
-            <!-- 如果有异常图片证据,这里可以展示 -->
             <img src="" alt="异常证据" />
           </div>
         </div>
-      </div>
+      </div> -->
 
       <!-- 录屏/切屏检测 -->
-      <div class="verification-item">
+      <!-- <div class="verification-item">
         <div class="item-header">
           <span class="item-title">录屏/切屏检测</span>
           <span class="item-status normal">无风险</span>
@@ -918,11 +1018,10 @@
         <div class="item-details">
           <p>系统监控显示考生未进行录屏或切屏操作,面试过程保持正常。</p>
           <div class="detail-images" v-if="false">
-            <!-- 如果有异常图片证据,这里可以展示 -->
             <img src="" alt="异常证据" />
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
   </div>
 </el-dialog>
@@ -1097,6 +1196,12 @@ export default {
       followUpQuestions: [], // 存储追问题
       noTableHeight: 0,
       showCheatVerification: false, // 添加控制作弊检测详情弹窗显示
+      abnormalPhotos: {
+        photos_by_type: {
+          identity_changed: []
+        }
+      },
+      emotionAnalysis: {},//情绪
     }
   },
   async created() {
@@ -1291,7 +1396,8 @@ export default {
               }
             }))
           }
-
+          this.abnormalPhotos = applicationData.application.abnormal_photos || {}
+          this.emotionAnalysis = applicationData.emotion_analysis || {}
           // 更新其他数据,添加空值检查
           if (applicationData.application) {
             this.totalScore = applicationData.application.comprehensive_score || 0
@@ -2777,6 +2883,9 @@ export default {
         &.normal {
           color: #67C23A;
         }
+        &.warning {
+        color: #f56c6c;
+    }
       }
 
       .separator {
@@ -3834,107 +3943,67 @@ export default {
 .eye-emotion-content {
   padding: 20px;
 
-  .emotion-data {
-    margin-bottom: 30px;
-
-    h3 {
-      font-size: 16px;
-      font-weight: bold;
-      color: #333;
-      margin-bottom: 20px;
-    }
+  h3 {
+    margin-bottom: 20px;
+    font-weight: 600;
+    color: #303133;
+  }
 
-    .data-charts {
-      display: grid;
-      grid-template-columns: repeat(2, 1fr);
-      gap: 20px;
-      margin-bottom: 20px;
+  h4 {
+    margin: 16px 0;
+    font-weight: 500;
+    color: #606266;
+  }
 
-      .chart-item {
-        background: #f8f8f8;
-        padding: 15px;
-        border-radius: 8px;
+  .mt-4 {
+    margin-top: 16px;
+  }
 
-        .chart-title {
-          font-size: 14px;
-          color: #666;
-          margin-bottom: 10px;
-        }
-      }
-    }
+  .mt-2 {
+    margin-top: 8px;
   }
 
-  .emotion-analysis {
-    h3 {
-      font-size: 16px;
-      font-weight: bold;
-      color: #333;
-      margin-bottom: 20px;
-    }
+  .mt-1 {
+    margin-top: 4px;
+  }
 
-    .analysis-content {
-      background: #f8f8f8;
-      padding: 20px;
-      border-radius: 8px;
+  .mb-2 {
+    margin-bottom: 8px;
+  }
 
-      .main-emotion {
-        font-size: 14px;
-        color: #333;
-        margin-bottom: 15px;
-      }
+  .mr-2 {
+    margin-right: 8px;
+  }
 
-      .emotion-details {
-        margin-bottom: 20px;
+  .emotion-data {
+    margin-bottom: 24px;
+  }
 
-        .detail-item {
-          margin-bottom: 10px;
-          padding: 10px;
-          background: #fff;
-          border-radius: 6px;
-          
-          .emotion-type {
-            font-weight: 500;
-            color: #333;
-            margin-bottom: 5px;
-          }
+  .emotion-analysis {
+    .el-collapse {
+      border: none;
+    }
+  }
 
-          .emotion-desc {
-            color: #666;
-            font-size: 13px;
-          }
-        }
-      }
+  :deep(.el-descriptions) {
+    padding: 16px;
+    background-color: #f8f9fa;
+    border-radius: 4px;
+  }
 
-      .suggestions {
-        .suggestion-title {
-          font-weight: 500;
-          color: #333;
-          margin-bottom: 10px;
-        }
+  :deep(.el-timeline-item__content) {
+    color: #606266;
+  }
 
-        .suggestion-list {
-          list-style: none;
-          padding: 0;
+  :deep(.el-collapse-item__header) {
+    font-weight: 500;
+  }
 
-          li {
-            color: #666;
-            font-size: 13px;
-            margin-bottom: 8px;
-            padding-left: 15px;
-            position: relative;
-
-            &:before {
-              content: "•";
-              position: absolute;
-              left: 0;
-              color: #333;
-            }
-          }
-        }
-      }
-    }
+  :deep(.el-tag) {
+    margin: 4px;
   }
 }
+
 // 移动端适配
 @media screen and (max-width: 768px) {
   .qa-section {

+ 31 - 3
src/views/position/detail/index.vue

@@ -616,10 +616,21 @@
                   <el-icon><Plus /></el-icon>
                   自定义题目
               </el-button>
-                <el-button  plain class="custom-btn" >
+              <el-button  plain class="custom-btn" >
                   <el-icon><Document /></el-icon>
                   导入题目
-                </el-button>
+              </el-button>
+              <el-button 
+                v-if="chatQuestions.length > 0"
+                type="primary" 
+                plain 
+                class="custom-btn"
+                :loading="autoGenerateLoading"
+                @click="handleAutoGenerate"
+              >
+                <el-icon><Refresh /></el-icon>
+                重新生成
+              </el-button>
                 
               <!-- 添加得分题和色盲题开关 -->
               <div class="question-switches">
@@ -4566,6 +4577,23 @@ const editAIVideo = async (step: AIVideoStep) => {
     currentStep.value = 1;
     
     try {
+      // 获取当前职位下已生成的问题
+      const questionResponse = await GetDraftList({
+        position_id: route.query.id,
+        page: 1,
+        limit: 20
+      });
+      
+      if (questionResponse && questionResponse.data && questionResponse.data.dataList) {
+        chatQuestions.value = questionResponse.data.dataList.map((item:any)=>{
+          item.content=item.question;
+          item.title=item.question;
+          item.competency_tags=item.competency_tag_info
+          item.source='ai_generated';
+          return item;
+        });
+      }
+
       // 首先尝试获取已有的职位标签
       const existingTags = await GetPositionTags({position_id:route.query.id,page:1,limit:50});
       console.log('existingTags', existingTags);
@@ -5038,7 +5066,7 @@ const delay = (ms: number): Promise<void> => {
 let pollingController: { cancelled: boolean } | null = null;
 
 // 轮询获取草稿列表的函数
-const pollForDraftList = async (params: { tenant_id: number; page: number; limit: number; position_id: string }, maxAttempts: number = 100, interval: number = 2000): Promise<any> => {
+const pollForDraftList = async (params: { tenant_id: number; page: number; limit: number; position_id: string }, maxAttempts: number = 150, interval: number = 2000): Promise<any> => {
   let attempts = 0;
   
   // 创建轮询控制器

+ 1 - 1
src/views/system/home/index.vue

@@ -754,7 +754,7 @@ export default defineComponent({
 			const routes = [
 				'/JobApplication/list/index', // 申请总数
 				'/position/list',    // 职位总数
-				'/talent/list/index',    // 问题总数
+				'/questionBank/list',    // 问题总数
 				'/JobApplication/list/index'       // 报告总数
 			];