|
@@ -340,9 +340,9 @@
|
|
<div v-for="(step, index) in recruitmentProcess" :key="step.id">
|
|
<div v-for="(step, index) in recruitmentProcess" :key="step.id">
|
|
<!-- 添加在每个步骤上方的加号按钮 -->
|
|
<!-- 添加在每个步骤上方的加号按钮 -->
|
|
<div class="add-process-btn-top" v-if="index === 0 || true">
|
|
<div class="add-process-btn-top" v-if="index === 0 || true">
|
|
- <el-button type="text" @click="showStepOptions(index, $event)">
|
|
|
|
|
|
+ <!-- <el-button type="text" @click="showStepOptions(index, $event)">
|
|
<el-icon><Plus /></el-icon>
|
|
<el-icon><Plus /></el-icon>
|
|
- </el-button>
|
|
|
|
|
|
+ </el-button> -->
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div
|
|
<div
|
|
@@ -379,9 +379,9 @@
|
|
|
|
|
|
<!-- 最后一个步骤下方的加号按钮 -->
|
|
<!-- 最后一个步骤下方的加号按钮 -->
|
|
<div class="add-process-btn">
|
|
<div class="add-process-btn">
|
|
- <el-button type="text" @click="showStepOptions(recruitmentProcess.length, $event)">
|
|
|
|
|
|
+ <!-- <el-button type="text" @click="showStepOptions(recruitmentProcess.length, $event)">
|
|
<el-icon><Plus /></el-icon>
|
|
<el-icon><Plus /></el-icon>
|
|
- </el-button>
|
|
|
|
|
|
+ </el-button> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
@@ -650,7 +650,7 @@
|
|
:key="avatar.id"
|
|
:key="avatar.id"
|
|
class="avatar-item"
|
|
class="avatar-item"
|
|
:class="{ active: selectedInterviewer.id === avatar.id }"
|
|
:class="{ active: selectedInterviewer.id === avatar.id }"
|
|
- @click="selectedInterviewer = avatar"
|
|
|
|
|
|
+ @click="handleInterviewerSelect(avatar)"
|
|
>
|
|
>
|
|
<div class="avatar-wrapper">
|
|
<div class="avatar-wrapper">
|
|
<div class="avatar-circle" :class="{ selected: selectedInterviewer.id === avatar.id }">
|
|
<div class="avatar-circle" :class="{ selected: selectedInterviewer.id === avatar.id }">
|
|
@@ -673,22 +673,40 @@
|
|
<div class="interview-settings">
|
|
<div class="interview-settings">
|
|
<div class="setting-item">
|
|
<div class="setting-item">
|
|
<div class="setting-label">「回答视频保留」</div>
|
|
<div class="setting-label">「回答视频保留」</div>
|
|
- <el-switch v-model="settings.keepVideo" />
|
|
|
|
|
|
+ <el-switch
|
|
|
|
+ v-model="settings.keepVideo"
|
|
|
|
+ @change="(value) => handleSettingChange('keepVideo', value)"
|
|
|
|
+ />
|
|
<div class="setting-desc">当候选人不符合中断条件时,保留其回答视频</div>
|
|
<div class="setting-desc">当候选人不符合中断条件时,保留其回答视频</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="setting-item">
|
|
<div class="setting-item">
|
|
<div class="setting-label">「中断后作答限制」</div>
|
|
<div class="setting-label">「中断后作答限制」</div>
|
|
<div class="setting-options">
|
|
<div class="setting-options">
|
|
- <el-radio v-model="settings.interruptionMode" label="previous">从上一问重新作答</el-radio>
|
|
|
|
- <el-radio v-model="settings.interruptionMode" label="current">从当前问题作答</el-radio>
|
|
|
|
|
|
+ <el-radio
|
|
|
|
+ v-model="settings.interruptionMode"
|
|
|
|
+ label="previous"
|
|
|
|
+ @change="handleSettingChange('interruptionMode', settings.interruptionMode)"
|
|
|
|
+ >
|
|
|
|
+ 从上一问重新作答
|
|
|
|
+ </el-radio>
|
|
|
|
+ <el-radio
|
|
|
|
+ v-model="settings.interruptionMode"
|
|
|
|
+ label="current"
|
|
|
|
+ @change="handleSettingChange('interruptionMode', settings.interruptionMode)"
|
|
|
|
+ >
|
|
|
|
+ 从当前问题作答
|
|
|
|
+ </el-radio>
|
|
</div>
|
|
</div>
|
|
<div class="setting-desc"></div>
|
|
<div class="setting-desc"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="setting-item">
|
|
<div class="setting-item">
|
|
<div class="setting-label">「智慧追问」</div>
|
|
<div class="setting-label">「智慧追问」</div>
|
|
- <el-switch v-model="settings.smartFollowUp" />
|
|
|
|
|
|
+ <el-switch
|
|
|
|
+ v-model="settings.smartFollowUp"
|
|
|
|
+ @change="(value) => handleSettingChange('smartFollowUp', value)"
|
|
|
|
+ />
|
|
<div class="setting-desc">当候选人给出特殊关键词时,系统将自动追问</div>
|
|
<div class="setting-desc">当候选人给出特殊关键词时,系统将自动追问</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -1038,7 +1056,7 @@
|
|
<div class="field-config-panel">
|
|
<div class="field-config-panel">
|
|
<div class="panel-header">
|
|
<div class="panel-header">
|
|
<h3>字段显示配置</h3>
|
|
<h3>字段显示配置</h3>
|
|
- <el-button type="primary" size="small" @click="saveFieldConfig">保存配置</el-button>
|
|
|
|
|
|
+ <!-- <el-button type="primary" size="small" @click="saveFieldConfig">保存配置</el-button> -->
|
|
</div>
|
|
</div>
|
|
<div class="field-config-list">
|
|
<div class="field-config-list">
|
|
<!-- 基本信息部分 -->
|
|
<!-- 基本信息部分 -->
|
|
@@ -1195,7 +1213,7 @@
|
|
<template #footer>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<span class="dialog-footer">
|
|
<el-button @click="cancelDataCollection">取消</el-button>
|
|
<el-button @click="cancelDataCollection">取消</el-button>
|
|
- <el-button type="primary" @click="confirmDataCollection">确认</el-button>
|
|
|
|
|
|
+ <el-button type="primary" @click="saveFieldConfig">确认</el-button><!-- confirmDataCollection -->
|
|
</span>
|
|
</span>
|
|
</template>
|
|
</template>
|
|
</el-dialog>
|
|
</el-dialog>
|
|
@@ -1427,7 +1445,7 @@ import { ElMessage, ElMessageBox, ElDrawer } from 'element-plus';
|
|
import * as api from '../list/api';
|
|
import * as api from '../list/api';
|
|
import { Plus, Document, ArrowDown, Edit, Delete, InfoFilled, QuestionFilled, Search, Rank } from '@element-plus/icons-vue';
|
|
import { Plus, Document, ArrowDown, Edit, Delete, InfoFilled, QuestionFilled, Search, Rank } from '@element-plus/icons-vue';
|
|
import { GetList } from '../../questionBank/classList/api';
|
|
import { GetList } from '../../questionBank/classList/api';
|
|
-import { GetCompetencyList,GetQuestionList,GenerateQuestions,GetDraftList,SaveDraft,GetDigitalList,CreateConfig,GetConfig } from './api';
|
|
|
|
|
|
+import { GetCompetencyList,GetQuestionList,GenerateQuestions,GetDraftList,SaveDraft,GetDigitalList,CreateConfig,GetConfig,UpdateConfig } from './api';
|
|
import draggable from 'vuedraggable';
|
|
import draggable from 'vuedraggable';
|
|
import { updateFieldConfig } from './utils';
|
|
import { updateFieldConfig } from './utils';
|
|
import type { ProfileFieldsConfig } from './types';
|
|
import type { ProfileFieldsConfig } from './types';
|
|
@@ -1915,9 +1933,13 @@ const addSelectedStepType = async (type: string, label: string) => {
|
|
if (type === 'data_collection') {
|
|
if (type === 'data_collection') {
|
|
const response = await GetConfig(route.query.id);
|
|
const response = await GetConfig(route.query.id);
|
|
if (response && response.data) {
|
|
if (response && response.data) {
|
|
|
|
+ getConfigId.value=response.data.id;
|
|
updateFieldConfig(fieldConfig, response.data.profile_fields_config as ProfileFieldsConfig);
|
|
updateFieldConfig(fieldConfig, response.data.profile_fields_config as ProfileFieldsConfig);
|
|
showDataCollectionDialog.value = true;
|
|
showDataCollectionDialog.value = true;
|
|
showOptionsMenu.value = false;
|
|
showOptionsMenu.value = false;
|
|
|
|
+ }else{
|
|
|
|
+ showDataCollectionDialog.value = true;
|
|
|
|
+ showOptionsMenu.value = false;
|
|
}
|
|
}
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -1982,14 +2004,25 @@ const nextStep = async () => {
|
|
await SaveDraft({
|
|
await SaveDraft({
|
|
draft_ids: chatQuestions.value.map((draft: any) => draft.id)
|
|
draft_ids: chatQuestions.value.map((draft: any) => draft.id)
|
|
});
|
|
});
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error('保存暂存题目失败:', error);
|
|
console.error('保存暂存题目失败:', error);
|
|
ElMessage.error('保存暂存题目失败');
|
|
ElMessage.error('保存暂存题目失败');
|
|
return; // 如果保存失败,不进入下一步
|
|
return; // 如果保存失败,不进入下一步
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ // 如果从第三步完成配置,验证面试设置
|
|
|
|
+ if (currentStep.value === 3) {
|
|
|
|
+ const validation = validateInterviewSettings();
|
|
|
|
+ if (!validation.isValid) {
|
|
|
|
+ validation.messages.forEach(message => {
|
|
|
|
+ ElMessage.warning(message);
|
|
|
|
+ });
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 打印完整的配置信息
|
|
|
|
+ const completeConfig = getCompleteInterviewConfig();
|
|
|
|
+ console.log('完整的面试配置:', completeConfig);
|
|
}
|
|
}
|
|
currentStep.value++;
|
|
currentStep.value++;
|
|
} else {
|
|
} else {
|
|
@@ -2006,17 +2039,38 @@ const prevStep = () => {
|
|
};
|
|
};
|
|
|
|
|
|
// 完成配置
|
|
// 完成配置
|
|
-const finishConfig = () => {
|
|
|
|
- // 创建新的AI视频步骤
|
|
|
|
- const newStep = {
|
|
|
|
- id: Date.now(),
|
|
|
|
- name: 'AI视频',
|
|
|
|
- description: `已选择${selectedAbilities.value.length}项能力测评`,
|
|
|
|
- active: true,
|
|
|
|
- type: 'ai_video',
|
|
|
|
- abilities: selectedAbilities.value.map(a => a.value)
|
|
|
|
|
|
+const finishConfig = async () => {
|
|
|
|
+ // 获取选中的面试官信息
|
|
|
|
+ const selectedInterviewerInfo = {
|
|
|
|
+ id: selectedInterviewer.value.id,
|
|
|
|
+ name: selectedInterviewer.value.name,
|
|
|
|
+ avatar_url: selectedInterviewer.value.avatar_url || selectedInterviewer.value.image
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ // 获取面试设置选项
|
|
|
|
+ const interviewSettings = getInterviewSettings();
|
|
|
|
+ const response = await CreateConfig({
|
|
|
|
+ position_id:route.query.id,
|
|
|
|
+ digital_human_id:selectedInterviewer.value.id,
|
|
|
|
+ })
|
|
|
|
+ console.log(response);
|
|
|
|
+ console.log('保存时选中的面试官:', selectedInterviewerInfo);
|
|
|
|
+ console.log('保存时的面试设置:', interviewSettings);
|
|
|
|
+
|
|
|
|
+ // 创建新的AI视频步骤
|
|
|
|
+ const newStep = {
|
|
|
|
+ id: Date.now(),
|
|
|
|
+ name: 'AI视频',
|
|
|
|
+ description: `已选择${selectedAbilities.value.length}项能力测评`,
|
|
|
|
+ active: true,
|
|
|
|
+ type: 'ai_video',
|
|
|
|
+ abilities: selectedAbilities.value.map(a => a.value),
|
|
|
|
+ // 添加面试官信息
|
|
|
|
+ interviewer: selectedInterviewerInfo,
|
|
|
|
+ // 添加面试设置信息
|
|
|
|
+ settings: interviewSettings
|
|
|
|
+ };
|
|
|
|
+
|
|
// 添加到流程中
|
|
// 添加到流程中
|
|
recruitmentProcess.splice(currentAddIndex.value, 0, newStep);
|
|
recruitmentProcess.splice(currentAddIndex.value, 0, newStep);
|
|
|
|
|
|
@@ -2759,20 +2813,25 @@ const saveCompetency = async () => {
|
|
|
|
|
|
// 在setup中添加
|
|
// 在setup中添加
|
|
const currentEditingStep = ref<AIVideoStep | null>(null);
|
|
const currentEditingStep = ref<AIVideoStep | null>(null);
|
|
-
|
|
|
|
|
|
+/* 获取已配置ID */
|
|
|
|
+const getConfigId =ref(0)
|
|
// 编辑AI视频
|
|
// 编辑AI视频
|
|
const editAIVideo = async (step: AIVideoStep) => {
|
|
const editAIVideo = async (step: AIVideoStep) => {
|
|
// 添加资料收集类型的处理
|
|
// 添加资料收集类型的处理
|
|
if (step.name === '资料收集') {
|
|
if (step.name === '资料收集') {
|
|
const response = await GetConfig(route.query.id);
|
|
const response = await GetConfig(route.query.id);
|
|
if (response && response.data) {
|
|
if (response && response.data) {
|
|
|
|
+ getConfigId.value=response.data.id;
|
|
updateFieldConfig(fieldConfig, response.data.profile_fields_config as ProfileFieldsConfig);
|
|
updateFieldConfig(fieldConfig, response.data.profile_fields_config as ProfileFieldsConfig);
|
|
showDataCollectionDialog.value = true;
|
|
showDataCollectionDialog.value = true;
|
|
showOptionsMenu.value = false;
|
|
showOptionsMenu.value = false;
|
|
|
|
+ }else{
|
|
|
|
+ showDataCollectionDialog.value = true;
|
|
|
|
+ showOptionsMenu.value = false;
|
|
}
|
|
}
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if (step.name === 'AI视频') {
|
|
if (step.name === 'AI视频') {
|
|
currentEditingStep.value = step;
|
|
currentEditingStep.value = step;
|
|
showAIVideoDialog.value = true;
|
|
showAIVideoDialog.value = true;
|
|
@@ -2856,6 +2915,100 @@ const interviewerAvatars = ref([
|
|
|
|
|
|
const selectedInterviewer = ref(interviewerAvatars.value[0]);
|
|
const selectedInterviewer = ref(interviewerAvatars.value[0]);
|
|
|
|
|
|
|
|
+// 添加面试官选择处理函数
|
|
|
|
+const handleInterviewerSelect = (avatar: any) => {
|
|
|
|
+ // 更新选中的面试官
|
|
|
|
+ selectedInterviewer.value = avatar;
|
|
|
|
+
|
|
|
|
+ // 打印选中的面试官信息(调试用)
|
|
|
|
+ console.log('选中的面试官信息:', {
|
|
|
|
+ id: avatar.id,
|
|
|
|
+ name: avatar.name,
|
|
|
|
+ avatar_url: avatar.avatar_url || avatar.image
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 可以在这里添加其他处理逻辑
|
|
|
|
+ // 比如:保存到后端、触发其他事件等
|
|
|
|
+
|
|
|
|
+ // 提示用户
|
|
|
|
+ ElMessage.success(`已选择面试官:${avatar.name}`);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 获取面试设置选项的函数
|
|
|
|
+const getInterviewSettings = () => {
|
|
|
|
+ const interviewSettings = {
|
|
|
|
+ // 回答视频保留设置
|
|
|
|
+ keepVideo: settings.keepVideo,
|
|
|
|
+ // 中断后作答限制模式
|
|
|
|
+ interruptionMode: settings.interruptionMode,
|
|
|
|
+ // 智慧追问设置
|
|
|
|
+ smartFollowUp: settings.smartFollowUp
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ console.log('当前面试设置:', interviewSettings);
|
|
|
|
+ return interviewSettings;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 处理面试设置变化的函数
|
|
|
|
+const handleSettingChange = (settingType: string, value: any) => {
|
|
|
|
+ console.log(`面试设置 ${settingType} 已更改为:`, value);
|
|
|
|
+
|
|
|
|
+ // 根据不同的设置类型进行处理
|
|
|
|
+ switch (settingType) {
|
|
|
|
+ case 'keepVideo':
|
|
|
|
+ console.log('回答视频保留设置:', value ? '开启' : '关闭');
|
|
|
|
+ break;
|
|
|
|
+ case 'interruptionMode':
|
|
|
|
+ console.log('中断后作答限制模式:', value === 'previous' ? '从上一问重新作答' : '从当前问题作答');
|
|
|
|
+ break;
|
|
|
|
+ case 'smartFollowUp':
|
|
|
|
+ console.log('智慧追问设置:', value ? '开启' : '关闭');
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 可以在这里添加其他处理逻辑
|
|
|
|
+ // 比如:实时保存到后端、触发相关事件等
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 验证面试设置配置完整性
|
|
|
|
+const validateInterviewSettings = () => {
|
|
|
|
+ const currentSettings = getInterviewSettings();
|
|
|
|
+
|
|
|
|
+ // 检查必要设置是否已配置
|
|
|
|
+ const validation = {
|
|
|
|
+ isValid: true,
|
|
|
|
+ messages: [] as string[]
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 这里可以添加具体的验证逻辑
|
|
|
|
+ if (currentSettings.interruptionMode === '') {
|
|
|
|
+ validation.isValid = false;
|
|
|
|
+ validation.messages.push('请选择中断后作答限制模式');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return validation;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 获取完整的面试配置信息
|
|
|
|
+const getCompleteInterviewConfig = () => {
|
|
|
|
+ return {
|
|
|
|
+ // 面试官信息
|
|
|
|
+ interviewer: {
|
|
|
|
+ id: selectedInterviewer.value.id,
|
|
|
|
+ name: selectedInterviewer.value.name,
|
|
|
|
+ avatar_url: selectedInterviewer.value.avatar_url || selectedInterviewer.value.image
|
|
|
|
+ },
|
|
|
|
+ // 面试设置
|
|
|
|
+ settings: getInterviewSettings(),
|
|
|
|
+ // 选中的能力标签
|
|
|
|
+ abilities: selectedAbilities.value,
|
|
|
|
+ // 题目列表
|
|
|
|
+ questions: chatQuestions.value,
|
|
|
|
+ // 当前步骤
|
|
|
|
+ currentStep: currentStep.value
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
/* 获取数字人列表 */
|
|
/* 获取数字人列表 */
|
|
const getDigitalList = async () => {
|
|
const getDigitalList = async () => {
|
|
const response = await GetDigitalList({page:1,pageSize:1000});
|
|
const response = await GetDigitalList({page:1,pageSize:1000});
|
|
@@ -3110,8 +3263,8 @@ const saveFieldConfig = async () => {
|
|
// 构建配置数据
|
|
// 构建配置数据
|
|
const configData = {
|
|
const configData = {
|
|
position_id: route.query.id, // 职位ID
|
|
position_id: route.query.id, // 职位ID
|
|
- config_name: "咖啡师配置", // 配置名称
|
|
|
|
- digital_human_id: 4, // 数字人ID
|
|
|
|
|
|
+ /* config_name: "咖啡师配置", // 配置名称
|
|
|
|
+ digital_human_id: 4, // 数字人ID */
|
|
|
|
|
|
// 简历上传配置
|
|
// 简历上传配置
|
|
require_resume_upload: true, // 是否要求上传简历
|
|
require_resume_upload: true, // 是否要求上传简历
|
|
@@ -3206,10 +3359,32 @@ const saveFieldConfig = async () => {
|
|
is_active: true,
|
|
is_active: true,
|
|
is_template: false
|
|
is_template: false
|
|
};
|
|
};
|
|
|
|
+ if(getConfigId.value==0){
|
|
|
|
+ }else{
|
|
|
|
+ configData.id=getConfigId.value;
|
|
|
|
+ }
|
|
|
|
|
|
// 调用后端 API
|
|
// 调用后端 API
|
|
- await CreateConfig(configData);
|
|
|
|
- ElMessage.success('配置保存成功');
|
|
|
|
|
|
+ if(getConfigId.value==0){
|
|
|
|
+ const response = await CreateConfig(configData);
|
|
|
|
+ if(response.code==2000){
|
|
|
|
+ ElMessage.success('配置保存成功');
|
|
|
|
+ showDataCollectionDialog.value = false;
|
|
|
|
+ showOptionsMenu.value = false;
|
|
|
|
+ }else{
|
|
|
|
+ ElMessage.error('配置保存失败');
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ const response = await UpdateConfig(configData);
|
|
|
|
+ if(response.code==2000){
|
|
|
|
+ ElMessage.success('配置保存成功');
|
|
|
|
+ showDataCollectionDialog.value = false;
|
|
|
|
+ showOptionsMenu.value = false;
|
|
|
|
+ }else{
|
|
|
|
+ ElMessage.error('配置保存失败');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error('保存配置失败:', error);
|
|
console.error('保存配置失败:', error);
|
|
ElMessage.error('配置保存失败');
|
|
ElMessage.error('配置保存失败');
|