yangg 2 месяцев назад
Родитель
Сommit
43b4c23cdc

+ 8 - 1
src/utils/service.ts

@@ -88,7 +88,14 @@ function createService() {
 						});
 						errorCreate(`${dataAxios.msg}: ${response.config.url}`);
 						break;
-					case 2000:
+					case 200 :
+						// @ts-ignore
+						if (response.config.unpack === false) {
+							//如果不需要解包
+							return dataAxios;
+						}
+						return dataAxios;
+					case 2000 :
 						// @ts-ignore
 						if (response.config.unpack === false) {
 							//如果不需要解包

+ 18 - 5
src/views/position/detail/api.ts

@@ -86,13 +86,26 @@ export function AddDocument(obj: AddReq) {
 	});
   }
 
-export function DelObj(data:any) {
+/* 生成开场白 */
+export function GetOpeningSpeech(obj: AddReq) {
 	return request({
-		url:`/api/system/competency/delete`,
-		method: 'post',
-		data: {...data,tenant_id:1},
+	  url: '/api/system/digital_human/generate_opening_video',
+	  method: 'post',
+	  data: {...obj,tenant_id:"1"},
+	  timeout: 0
 	});
-}
+  }
+
+ /* 生成视频 */
+ export function GetVideo(obj: AddReq) {
+	return request({
+	  url: '/api/system/digital_human/generate_question_videos',
+	  method: 'post',
+	  data: {...obj,tenant_id:"1"},
+	  timeout: 0
+	});
+  }
+
 export function GetPermission(query: UserPageQuery) {
     return request({
         url: '/api/system/competency/list',

+ 1 - 0
src/views/position/detail/index.css

@@ -58,6 +58,7 @@
   display: flex;
   margin-bottom: 20px;
   line-height: 1.5;
+  align-items: center;
 }
 
 .detail-label {

+ 489 - 2
src/views/position/detail/index.vue

@@ -3,7 +3,7 @@
     <div class="header-container">
       <page-header title="测试" @back="goBack" />
       <div class="header-actions">
-        <el-button type="primary" @click="handleEdit">编辑</el-button>
+        <el-button type="primary" @click="handleEdit">保存</el-button>
         <el-button @click="handleShare">分享职位</el-button>
       </div>
     </div>
@@ -326,6 +326,13 @@
             </div>
           </div>
         </el-card>
+        <el-card class="status-card">
+          <div class="section-title">
+            <div class="section-line"></div>
+            <span>面试设置</span>
+            <el-button type="text" class="edit-btn" @click="handleEditInterview">编辑</el-button>
+          </div>
+        </el-card>
       </div>
     </div>
 
@@ -1441,6 +1448,169 @@
         </span>
       </template>
     </el-drawer>
+
+    <!-- 面试设置弹窗 -->
+    <el-dialog
+      title="面试设置"
+      v-model="showInterviewSettingsDialog"
+      width="60%"
+      :close-on-click-modal="false"
+      class="interview-settings-dialog"
+    >
+      <div class="interview-settings-content">
+        <!-- 防作弊设置 -->
+        <div class="settings-section">
+          <div class="section-header">
+            <span class="section-title">防作弊设置</span>
+          </div>
+          
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">「人脸一致性检测」</span>
+              <p class="setting-desc">该强调,非本人操作人脸可视化提醒和警示,默认开启</p>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.faceConsistency" /> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">「 防截屏 / 录屏 」</span>
+              <p class="setting-desc">该强调,非本人前来体验截屏录屏以上行为,为防止提醒</p>
+            </div>
+          <!--   <el-switch v-model="interviewSettings.preventScreenshot" /> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">「 切 屏 预 制 」</span>
+              <p class="setting-desc">该强调,非本人前使用切屏处理用页面,为防作弊警示</p>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.screenSwitchAlert">
+              <span class="switch-desc">切屏超过 2 次关闭,作答本关面试</span>
+            </el-switch> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">「 情 绪 识 别」</span>
+              <p class="setting-desc">该现已,条件较差以及入侵过程中的作弊,条件等好等</p>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.positionDetection" /> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">「 眼 动 检 测 」</span>
+              <p class="setting-desc">视实检测候选人的眼动频率进行分析,页面等较等</p>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.eyeMovementDetection" /> -->
+          </div>
+        </div>
+
+        <!-- 其他设置 -->
+        <div class="settings-section">
+          <div class="section-header">
+            <span class="section-title">其他设置</span>
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">复习面试</span>
+              <p class="setting-desc">选取后,将允许面试者进行重复面试</p>
+            </div>
+            <el-switch v-model="interviewSettings.reviewInterview" />
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">开场词</span>
+              <div v-if="!isEditingBeforeStart" class="setting-desc-container">
+                <p class="setting-desc">{{ interviewSettings.beforeStartText }}</p>
+                <el-button type="text" class="edit-desc-btn" @click="startEditBeforeStart">
+                  <el-icon><Edit /></el-icon>
+                </el-button>
+              </div>
+              <div v-else class="desc-edit-container">
+                <el-input
+                  v-model="editingBeforeStartText"
+                  type="textarea"
+                  rows="2"
+                  placeholder="请输入开始前提示内容"
+                  
+                  show-word-limit
+                /><!-- maxlength="100" -->
+                <div class="desc-edit-actions">
+                  <el-button size="small" @click="cancelEditBeforeStart">取消</el-button>
+                  <el-button size="small" type="primary" @click="saveBeforeStart">保存</el-button>
+                </div>
+              </div>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.beforeStart" /> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">开始后提醒</span>
+              <div v-if="!isEditingAfterStart" class="setting-desc-container">
+                <p class="setting-desc">{{ interviewSettings.afterStartText }}</p>
+                <el-button type="text" class="edit-desc-btn" @click="startEditAfterStart">
+                  <el-icon><Edit /></el-icon>
+                </el-button>
+              </div>
+              <div v-else class="desc-edit-container">
+                <el-input
+                  v-model="editingAfterStartText"
+                  type="textarea"
+                  rows="2"
+                  placeholder="请输入开始后提醒内容"
+                  maxlength="100"
+                  show-word-limit
+                />
+                <div class="desc-edit-actions">
+                  <el-button size="small" @click="cancelEditAfterStart">取消</el-button>
+                  <el-button size="small" type="primary" @click="saveAfterStart">保存</el-button>
+                </div>
+              </div>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.afterStartReminder" /> -->
+          </div>
+
+          <div class="setting-item">
+            <div class="setting-info">
+              <span class="setting-name">结束语</span>
+              <div v-if="!isEditingDisclaimer" class="setting-desc-container">
+                <p class="setting-desc">{{ interviewSettings.disclaimerText }}</p>
+                <el-button type="text" class="edit-desc-btn" @click="startEditDisclaimer">
+                  <el-icon><Edit /></el-icon>
+                </el-button>
+              </div>
+              <div v-else class="desc-edit-container">
+                <el-input
+                  v-model="editingDisclaimerText"
+                  type="textarea"
+                  rows="2"
+                  placeholder="请输入免责声明内容"
+                  maxlength="200"
+                  show-word-limit
+                />
+                <div class="desc-edit-actions">
+                  <el-button size="small" @click="cancelEditDisclaimer">取消</el-button>
+                  <el-button size="small" type="primary" @click="saveDisclaimer">保存</el-button>
+                </div>
+              </div>
+            </div>
+           <!--  <el-switch v-model="interviewSettings.disclaimer" /> -->
+          </div>
+        </div>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="cancelInterviewSettings">取消</el-button>
+          <el-button type="primary" @click="confirmInterviewSettings">完成</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -1453,7 +1623,8 @@ import { Plus, Document, ArrowDown, Edit, Delete, InfoFilled, QuestionFilled, Se
 import { GetList } from '../../questionBank/classList/api';
 import { GetCompetencyList,GetQuestionList,GenerateQuestions,
   GetDraftList,SaveDraft,GetDigitalList,
-  CreateConfig,GetConfig,UpdateConfig,AddDocument } from './api';
+  CreateConfig,GetConfig,UpdateConfig,AddDocument,
+  GetOpeningSpeech,GetVideo } from './api';
 import draggable from 'vuedraggable';
 import { updateFieldConfig } from './utils';
 import type { ProfileFieldsConfig } from './types';
@@ -2090,6 +2261,34 @@ const finishConfig = async () => {
     })  
     if(response.code==2000){
       ElMessage.success('配置保存成功');
+      
+      // 调用获取开场白和视频接口 - 顺序执行,间隔3-5秒
+      try {
+        const params = { config_id: response.data?.id || response.data };
+        
+        // 检查是否有开场白配置
+        const hasOpeningSpeech = interviewSettings.beforeStartText && 
+                                 interviewSettings.beforeStartText.trim() !== '';
+        
+        if (hasOpeningSpeech) {
+          // 有开场白配置,先请求获取开场白接口
+          await GetOpeningSpeech(params);
+          console.log('开场白生成成功');
+          
+          // 等待3-5秒(这里设置为4秒)
+          await new Promise(resolve => setTimeout(resolve, 4000));
+        } else {
+          console.log('无开场白配置,跳过开场白生成');
+        }
+        
+        // 请求获取视频接口
+        await GetVideo(params);
+        ElMessage.success('视频开始生成');
+      } catch (error) {
+        console.error('获取开场白和视频失败:', error);
+        ElMessage.warning('视频生成中,请稍后');
+      }
+      
       showAIVideoDialog.value = false;
       currentStep.value = 1;
     }else{
@@ -2102,6 +2301,34 @@ const finishConfig = async () => {
     })
     if(response.code==2000){
       ElMessage.success('配置保存成功');
+      
+      // 调用获取开场白和视频接口 - 顺序执行,间隔3-5秒
+      try {
+        const params = { config_id: getConfigId.value };
+        
+        // 检查是否有开场白配置
+        const hasOpeningSpeech = interviewSettings.beforeStartText && 
+                                 interviewSettings.beforeStartText.trim() !== '';
+        
+        if (hasOpeningSpeech) {
+          // 有开场白配置,先请求获取开场白接口
+          await GetOpeningSpeech(params);
+          console.log('开场白生成成功');
+          
+          // 等待3-5秒(这里设置为4秒)
+          await new Promise(resolve => setTimeout(resolve, 4000));
+        } else {
+          console.log('无开场白配置,跳过开场白生成');
+        }
+        
+        // 请求获取视频接口
+        await GetVideo(params);
+        ElMessage.success('视频开始生成');
+      } catch (error) {
+        console.error('获取开场白和视频失败:', error);
+        ElMessage.warning('视频生成中,请稍后');
+      }
+      
       showAIVideoDialog.value = false;
       currentStep.value = 1;
     }else{
@@ -3487,6 +3714,7 @@ const saveFieldConfig = async () => {
       ElMessage.error('配置保存失败');
     }
     }else{
+      //configData.digital_human_opening_speech='你好,我是本次面试的面试官,欢迎参加本公司的线上面试!面试预计需要15分钟,请你提前安排在网络良好、光线亮度合适、且相对安静的环境参加这次面试以免影响本次面试的结果。如果你在面试过程中遇到问题,请与我们的招聘人员联系。'
       const response = await UpdateConfig(configData);
       if(response.code==2000){
         ElMessage.success('配置保存成功');
@@ -3531,6 +3759,150 @@ const getSkillFields = () => {
   return skillFields;
 };
 
+// 面试设置弹窗
+const showInterviewSettingsDialog = ref(false);
+const interviewSettings = reactive({
+  faceConsistency: true,
+  preventScreenshot: true,
+  screenSwitchAlert: true,
+  positionDetection: true,
+  eyeMovementDetection: true,
+  reviewInterview: false,
+  beforeStart: true,
+  beforeStartText: '你好,我是本次面试的面试官,欢迎参加本公司的线上面试!面试预计需要15分钟,请你提前安排在网络良好、光线亮度合适、且相对安静的环境参加这次面试以免影响本次面试的结果。如果你在面试过程中遇到问题,请与我们的招聘人员联系。',
+  afterStartReminder: false,
+  afterStartText: '本次面试如有15分钟,高效调前后流程,安静好心理',
+  disclaimer: false,
+  disclaimerText: '本次面试已全部结束,感谢您的参与,请等待后续通知。'
+});
+
+// 编辑状态变量
+const isEditingBeforeStart = ref(false);
+const isEditingAfterStart = ref(false);
+const isEditingDisclaimer = ref(false);
+
+// 编辑中的文本内容
+const editingBeforeStartText = ref('');
+const editingAfterStartText = ref('');
+const editingDisclaimerText = ref('');
+
+// 取消面试设置
+const cancelInterviewSettings = () => {
+  showInterviewSettingsDialog.value = false;
+  // 重置编辑状态
+  isEditingBeforeStart.value = false;
+  isEditingAfterStart.value = false;
+  isEditingDisclaimer.value = false;
+  // 重置表单
+  Object.assign(interviewSettings, {
+    faceConsistency: true,
+    preventScreenshot: true,
+    screenSwitchAlert: true,
+    positionDetection: true,
+    eyeMovementDetection: true,
+    reviewInterview: false,
+    beforeStart: true,
+    beforeStartText: '你好,我想咨询其间的面试,欢迎来到大面试',
+    afterStartReminder: false,
+    afterStartText: '本次面试如有15分钟,高效调前后流程,安静好心理',
+    disclaimer: false,
+    disclaimerText: '本次面试已全部流程拍摄,面试者同意可为今后资讯调研'
+  });
+};
+
+// 确认面试设置
+const confirmInterviewSettings = () => {
+  // 这里可以添加保存面试设置的API调用
+  console.log('保存面试设置:', interviewSettings);
+  ElMessage.success('面试设置已保存');
+  showInterviewSettingsDialog.value = false;
+};
+
+// 编辑面试设置
+const handleEditInterview = () => {
+  GetConfig(route.query.id).then((res:any)=>{
+    showInterviewSettingsDialog.value = true;
+    getConfigId.value=res.data.id;
+    interviewSettings.beforeStartText = res.data.digital_human_opening_speech;
+  })
+  
+};
+
+// 开始前文本编辑方法
+const startEditBeforeStart = () => {
+  editingBeforeStartText.value = interviewSettings.beforeStartText;
+  isEditingBeforeStart.value = true;
+};
+
+const cancelEditBeforeStart = () => {
+  isEditingBeforeStart.value = false;
+  editingBeforeStartText.value = '';
+};
+
+const saveBeforeStart = async () => {
+  if (!editingBeforeStartText.value.trim()) {
+    ElMessage.warning('开始前提示内容不能为空');
+    return;
+  }
+  
+  interviewSettings.beforeStartText = editingBeforeStartText.value.trim();
+  const response = await UpdateConfig({
+    id:getConfigId.value,
+    digital_human_opening_speech:editingBeforeStartText.value.trim()
+  })
+  if(response.code==2000){
+    ElMessage.success('开始前提示内容已更新');
+    isEditingBeforeStart.value = false;
+  }else{
+    ElMessage.error('开始前提示内容更新失败');
+  }
+  
+};
+
+// 开始后提醒编辑方法
+const startEditAfterStart = () => {
+  editingAfterStartText.value = interviewSettings.afterStartText;
+  isEditingAfterStart.value = true;
+};
+
+const cancelEditAfterStart = () => {
+  isEditingAfterStart.value = false;
+  editingAfterStartText.value = '';
+};
+
+const saveAfterStart = () => {
+  if (!editingAfterStartText.value.trim()) {
+    ElMessage.warning('开始后提醒内容不能为空');
+    return;
+  }
+  
+  interviewSettings.afterStartText = editingAfterStartText.value.trim();
+  isEditingAfterStart.value = false;
+  ElMessage.success('开始后提醒内容已更新');
+};
+
+// 免责声明编辑方法
+const startEditDisclaimer = () => {
+  editingDisclaimerText.value = interviewSettings.disclaimerText;
+  isEditingDisclaimer.value = true;
+};
+
+const cancelEditDisclaimer = () => {
+  isEditingDisclaimer.value = false;
+  editingDisclaimerText.value = '';
+};
+
+const saveDisclaimer = () => {
+  if (!editingDisclaimerText.value.trim()) {
+    ElMessage.warning('免责声明内容不能为空');
+    return;
+  }
+  
+  interviewSettings.disclaimerText = editingDisclaimerText.value.trim();
+  isEditingDisclaimer.value = false;
+  ElMessage.success('免责声明内容已更新');
+};
+
 </script>
 
 <style scoped>
@@ -3702,4 +4074,119 @@ const getSkillFields = () => {
 .el-row {
   margin-bottom: 12px;
 }
+
+/* 面试设置弹窗样式 */
+.interview-settings-dialog {
+  :deep(.el-dialog__body) {
+    padding: 20px;
+  }
+}
+
+.interview-settings-content {
+  .settings-section {
+    margin-bottom: 24px;
+    
+    &:last-child {
+      margin-bottom: 0;
+    }
+    
+    .section-header {
+      margin-bottom: 16px;
+      
+      .section-title {
+        font-size: 16px;
+        font-weight: 500;
+        color: #303133;
+        padding-left: 10px;
+        border-left: 4px solid #0419a3;
+      }
+    }
+    
+    .setting-item {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+     padding: 0 30px;
+     border-radius: 20px;
+      border: 1px solid #f0f0f0;
+      
+      &:last-child {
+        border-bottom: none;
+      }
+      
+      .setting-info {
+        display: flex;
+        align-items: center;
+        flex: 1;
+        margin-right: 16px;
+        
+        .setting-name {
+          width: 30%;
+          font-size: 14px;
+          font-weight: 500;
+          color: #808080;
+          display: block;
+          margin-bottom: 4px;
+        }
+        
+        .setting-desc {
+          width: 70%;
+          font-size: 12px;
+          color: #909399;
+          margin: 0;
+          line-height: 1.4;
+        }
+      }
+      
+
+      
+      :deep(.el-switch.is-checked .el-switch__core) {
+        border-color: #0419a3;
+        background-color: #0419a3;
+      }
+      
+      .switch-desc {
+        font-size: 12px;
+        color: #909399;
+        margin-left: 8px;
+      }
+      
+      .setting-desc-container {
+        display: flex;
+        align-items: flex-start;
+        gap: 8px;
+        flex: 1;
+        
+        .setting-desc {
+          flex: 1;
+          margin: 0;
+        }
+        
+        .edit-desc-btn {
+          padding: 4px;
+          min-height: auto;
+          
+          &:hover {
+            color: #0419a3;
+          }
+        }
+      }
+      
+      .desc-edit-container {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        flex: 1;
+        
+        .desc-edit-actions {
+          display: flex;
+          gap: 8px;
+          margin-left: 15px;
+          margin-top: 8px;
+          justify-content: flex-end;
+        }
+      }
+    }
+  }
+}
 </style>