|
@@ -539,7 +539,7 @@
|
|
|
<el-button type="primary" plain size="small" class="customize-btn">定制面试间</el-button>
|
|
|
</div> -->
|
|
|
<div class="interview-content">
|
|
|
- <div class="interview-questions-container" :loading="autoGenerateLoading">
|
|
|
+ <div class="interview-questions-container" v-loading="autoGenerateLoading" element-loading-text="AI正在生成题目...">
|
|
|
<div class="no-questions" v-if="chatQuestions.length === 0">
|
|
|
<p>暂无题目</p>
|
|
|
<p class="ai-tip">点击"AI自动组题"按钮</p>
|
|
@@ -547,7 +547,7 @@
|
|
|
<el-button
|
|
|
type="success"
|
|
|
class="auto-generate-btn"
|
|
|
-
|
|
|
+ :loading="autoGenerateLoading"
|
|
|
@click="handleAutoGenerate"
|
|
|
>
|
|
|
AI自动组题
|
|
@@ -560,23 +560,32 @@
|
|
|
<div class="question-list" v-else>
|
|
|
<draggable
|
|
|
v-model="chatQuestions"
|
|
|
-
|
|
|
item-key="id"
|
|
|
handle=".drag-handle"
|
|
|
- @start="drag = true"
|
|
|
- @end="drag = false"
|
|
|
+ @start="handleDragStart"
|
|
|
+ @end="handleDragEnd"
|
|
|
+ animation="200"
|
|
|
+ ghost-class="sortable-ghost"
|
|
|
+ chosen-class="sortable-chosen"
|
|
|
+ drag-class="sortable-drag"
|
|
|
>
|
|
|
<template #item="{ element }">
|
|
|
<div class="question-card">
|
|
|
+ <div class="drag-handle">
|
|
|
+ <img src="../../../assets/dorp.png" alt="" class="dorp">
|
|
|
+ </div>
|
|
|
<div class="question-header">
|
|
|
<div class="question-title">
|
|
|
- <span class="question-number">题目{{ chatQuestions.indexOf(element) + 1 }}</span>
|
|
|
- <span><span style="color: #0017ab;">{{ '「'+element.question_form_name +'」'}}</span>{{ element.title }}</span>
|
|
|
+
|
|
|
+ <div class="title-content">
|
|
|
+ <span class="question-number">题目{{ chatQuestions.indexOf(element) + 1 }}</span>
|
|
|
+ <span><span style="color: #0017ab;">{{ '「'+element.question_form_name +'」'}}</span>{{ element.title }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div>
|
|
|
- <el-button type="text" @click="editQuestion(chatQuestions.indexOf(element))">
|
|
|
+ <div class="question-actions">
|
|
|
+ <!-- <el-button type="text" @click="editQuestion(chatQuestions.indexOf(element))">
|
|
|
<el-icon><Edit /></el-icon>
|
|
|
- </el-button>
|
|
|
+ </el-button> -->
|
|
|
<el-button type="text" @click="deleteQuestion(chatQuestions.indexOf(element))">
|
|
|
<el-icon><Delete /></el-icon>
|
|
|
</el-button>
|
|
@@ -630,15 +639,16 @@
|
|
|
<div class="multi-dimension-questions">
|
|
|
<div class="dimension-title">已选胜任力考核维度</div>
|
|
|
<div class="dimension-list">
|
|
|
- <div v-for="ability in selectedAbilities" :key="ability.value" class="dimension-item">
|
|
|
+ <div v-for="(ability, index) in selectedAbilities" :key="ability.value" class="dimension-item">
|
|
|
<div class="dimension-header">
|
|
|
<div class="dimension-info">
|
|
|
<span class="ability-name">{{ ability.label }}</span>
|
|
|
<img src="../../../assets/sheng.png" alt=""class="sheng">
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="dimension-content">
|
|
|
- <div class="ability-description">{{ "-" }}</div>
|
|
|
+ <div class="dimension-content" @click="removeSelectedAbility(index)" title="点击删除此胜任力标签">
|
|
|
+ <div class="ability-description">{{ "—" }}</div>
|
|
|
+ <!-- <el-icon class="delete-icon"><Delete /></el-icon> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -687,11 +697,12 @@
|
|
|
<div class="interview-settings">
|
|
|
<div class="setting-item">
|
|
|
<div class="setting-label">「回答视频保留」</div>
|
|
|
+ <div class="setting-desc">当候选人不符合中断条件时,保留其回答视频</div>
|
|
|
<el-switch
|
|
|
v-model="settings.keepVideo"
|
|
|
@change="(value) => handleSettingChange('keepVideo', value)"
|
|
|
/>
|
|
|
- <div class="setting-desc">当候选人不符合中断条件时,保留其回答视频</div>
|
|
|
+
|
|
|
</div>
|
|
|
|
|
|
<div class="setting-item">
|
|
@@ -699,29 +710,31 @@
|
|
|
<div class="setting-options">
|
|
|
<el-radio
|
|
|
v-model="settings.interruptionMode"
|
|
|
- label="previous"
|
|
|
+ label="current"
|
|
|
@change="handleSettingChange('interruptionMode', settings.interruptionMode)"
|
|
|
>
|
|
|
- 从上一问重新作答
|
|
|
+ 从当前问题重新作答
|
|
|
</el-radio>
|
|
|
<el-radio
|
|
|
v-model="settings.interruptionMode"
|
|
|
- label="current"
|
|
|
+ label="previous"
|
|
|
@change="handleSettingChange('interruptionMode', settings.interruptionMode)"
|
|
|
>
|
|
|
- 从当前问题作答
|
|
|
+ 从头作答
|
|
|
</el-radio>
|
|
|
+
|
|
|
</div>
|
|
|
<div class="setting-desc"></div>
|
|
|
</div>
|
|
|
|
|
|
<div class="setting-item">
|
|
|
<div class="setting-label">「智慧追问」</div>
|
|
|
+ <div class="setting-desc">当候选人给出特殊关键词时,系统将自动追问</div>
|
|
|
<el-switch
|
|
|
v-model="settings.smartFollowUp"
|
|
|
@change="(value) => handleSettingChange('smartFollowUp', value)"
|
|
|
/>
|
|
|
- <div class="setting-desc">当候选人给出特殊关键词时,系统将自动追问</div>
|
|
|
+
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -1947,6 +1960,10 @@ const getPositionDetail = async () => {
|
|
|
const response = await api.GetObj(id); // 使用 infoReq 而不是 id
|
|
|
if (response && response.data) {
|
|
|
Object.assign(positionData, response.data);
|
|
|
+ const config = await GetConfig(id);
|
|
|
+ if (config && config.data) {
|
|
|
+ getConfigId.value=config.data.id;
|
|
|
+ }
|
|
|
// 设置职位状态
|
|
|
positionStatus.value = positionData.status === 1;
|
|
|
}
|
|
@@ -2306,23 +2323,19 @@ const finishConfig = async () => {
|
|
|
try {
|
|
|
const params = { config_id: getConfigId.value };
|
|
|
|
|
|
- // 检查是否有开场白配置
|
|
|
- const hasOpeningSpeech = interviewSettings.beforeStartText &&
|
|
|
- interviewSettings.beforeStartText.trim() !== '';
|
|
|
+ // 第一步:请求获取开场白接口
|
|
|
+ console.log('开始生成开场白...');
|
|
|
+ await GetOpeningSpeech(params);
|
|
|
+ console.log('开场白生成成功');
|
|
|
|
|
|
- if (hasOpeningSpeech) {
|
|
|
- // 有开场白配置,先请求获取开场白接口
|
|
|
- await GetOpeningSpeech(params);
|
|
|
- console.log('开场白生成成功');
|
|
|
-
|
|
|
- // 等待3-5秒(这里设置为4秒)
|
|
|
- await new Promise(resolve => setTimeout(resolve, 4000));
|
|
|
- } else {
|
|
|
- console.log('无开场白配置,跳过开场白生成');
|
|
|
- }
|
|
|
+ // 第二步:等待3-5秒(这里设置为4秒)
|
|
|
+ console.log('等待4秒后开始生成视频...');
|
|
|
+ await delay(4000);
|
|
|
|
|
|
- // 请求获取视频接口
|
|
|
+ // 第三步:请求获取视频接口
|
|
|
+ console.log('开始生成视频...');
|
|
|
await GetVideo(params);
|
|
|
+ console.log('视频生成请求已发送');
|
|
|
ElMessage.success('视频开始生成');
|
|
|
} catch (error) {
|
|
|
console.error('获取开场白和视频失败:', error);
|
|
@@ -3318,6 +3331,24 @@ const validateInterviewSettings = () => {
|
|
|
return validation;
|
|
|
};
|
|
|
|
|
|
+// 拖拽开始处理
|
|
|
+const handleDragStart = (evt: any) => {
|
|
|
+ drag.value = true;
|
|
|
+ console.log('开始拖拽题目:', evt);
|
|
|
+};
|
|
|
+
|
|
|
+// 拖拽结束处理
|
|
|
+const handleDragEnd = (evt: any) => {
|
|
|
+ drag.value = false;
|
|
|
+ console.log('拖拽结束:', evt);
|
|
|
+ console.log('当前题目顺序:', chatQuestions.value);
|
|
|
+
|
|
|
+ // 这里可以调用API保存新的题目顺序
|
|
|
+ // await saveQuestionOrder(chatQuestions.value);
|
|
|
+
|
|
|
+ ElMessage.success('题目顺序已更新');
|
|
|
+};
|
|
|
+
|
|
|
// 获取完整的面试配置信息
|
|
|
const getCompleteInterviewConfig = () => {
|
|
|
return {
|
|
@@ -3361,9 +3392,44 @@ const handleCompetencyChange = (item: any, val: boolean) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+// 删除选中的胜任力标签
|
|
|
+const removeSelectedAbility = (index: number) => {
|
|
|
+ ElMessageBox.confirm('确认删除这个胜任力标签吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ // 获取被删除的能力标签
|
|
|
+ const removedAbility = selectedAbilities.value[index];
|
|
|
+
|
|
|
+ // 从 selectedAbilities 数组中移除
|
|
|
+ selectedAbilities.value.splice(index, 1);
|
|
|
+
|
|
|
+ // 同时从 selectedDimensions 数组中移除对应项
|
|
|
+ selectedDimensions.value = selectedDimensions.value.filter(
|
|
|
+ dimension => dimension.name !== removedAbility.label
|
|
|
+ );
|
|
|
+
|
|
|
+ // 更新对应的checkbox状态 - 将对应的competencyTags项的selected设为false
|
|
|
+ const competencyTag = competencyTags.value.find((tag: any) => tag.name === removedAbility.label);
|
|
|
+ if (competencyTag) {
|
|
|
+ competencyTag.selected = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessage.success('胜任力标签已删除');
|
|
|
+ }).catch(() => {
|
|
|
+ // 用户取消删除
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
// 在 script setup 部分添加以下代码
|
|
|
const selectedDimensions = ref<Array<{name: string, description: string}>>([]);
|
|
|
|
|
|
+// 创建延迟函数
|
|
|
+const delay = (ms: number): Promise<void> => {
|
|
|
+ return new Promise(resolve => setTimeout(resolve, ms));
|
|
|
+};
|
|
|
+
|
|
|
// 在 script setup 中添加以下内容
|
|
|
// 题目选择相关的响应式变量
|
|
|
const showQuestionSelectDialog = ref(false);
|
|
@@ -3846,17 +3912,54 @@ const saveBeforeStart = async () => {
|
|
|
}
|
|
|
|
|
|
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('开始前提示内容更新失败');
|
|
|
- }
|
|
|
|
|
|
+ try {
|
|
|
+ let response;
|
|
|
+ console.log(getConfigId.value);
|
|
|
+ // 根据getConfigId的值决定调用哪个接口
|
|
|
+ if (getConfigId.value == 0) {
|
|
|
+ // getConfigId不等于0时,调用CreateConfig接口
|
|
|
+ response = await CreateConfig({
|
|
|
+ position_id: route.query.id,
|
|
|
+ digital_human_opening_speech: editingBeforeStartText.value.trim()
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.code === 2000) {
|
|
|
+ // 创建成功后更新getConfigId
|
|
|
+ getConfigId.value = response.data?.id || response.data;
|
|
|
+ ElMessage.success('开始前提示内容已创建');
|
|
|
+ isEditingBeforeStart.value = false;
|
|
|
+ } else {
|
|
|
+ ElMessage.error('开始前提示内容创建失败');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // getConfigId等于0时,调用UpdateConfig接口
|
|
|
+ response = await UpdateConfig({
|
|
|
+ id: getConfigId.value,
|
|
|
+ digital_human_opening_speech: editingBeforeStartText.value.trim()
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.code === 2000) {
|
|
|
+ ElMessage.success('开始前提示内容已更新');
|
|
|
+ isEditingBeforeStart.value = false;
|
|
|
+
|
|
|
+ // 调用GetOpeningSpeech接口
|
|
|
+ try {
|
|
|
+ const params = { config_id: getConfigId.value };
|
|
|
+ await GetOpeningSpeech(params);
|
|
|
+ console.log('开场白生成成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取开场白失败:', error);
|
|
|
+ ElMessage.warning('开场白生成中,请稍后');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ElMessage.error('开始前提示内容更新失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存开始前提示内容失败:', error);
|
|
|
+ ElMessage.error('保存失败,请重试');
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 开始后提醒编辑方法
|
|
@@ -3952,21 +4055,58 @@ const saveDisclaimer = () => {
|
|
|
&:hover {
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- .drag-handle {
|
|
|
- position: absolute;
|
|
|
- left: -20px;
|
|
|
- top: 50%;
|
|
|
- transform: translateY(-50%);
|
|
|
- cursor: move;
|
|
|
- color: #909399;
|
|
|
- padding: 4px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- color: #409EFF;
|
|
|
+/* 拖拽状态样式 */
|
|
|
+.sortable-ghost {
|
|
|
+ opacity: 0.5;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border: 2px dashed #c0c4cc;
|
|
|
+}
|
|
|
+
|
|
|
+// .sortable-chosen {
|
|
|
+// cursor: grabbing;
|
|
|
+// transform: rotate(5deg);
|
|
|
+// }
|
|
|
+
|
|
|
+.sortable-drag {
|
|
|
+ cursor: grabbing;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
|
+ transform: rotate(5deg);
|
|
|
+}
|
|
|
+
|
|
|
+ .question-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .question-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 12px;
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+
|
|
|
+ .title-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 10px;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .question-number {
|
|
|
+ color: #909399;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 4px;
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
.question-info {
|
|
|
.info-item {
|
|
@@ -3982,17 +4122,7 @@ const saveDisclaimer = () => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-.question-title {
|
|
|
- font-weight: 500;
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- gap: 10px;
|
|
|
- flex-direction: column;
|
|
|
- .question-number {
|
|
|
- color: #909399;
|
|
|
- margin-right: 8px;
|
|
|
- }
|
|
|
-}
|
|
|
+
|
|
|
|
|
|
.field-config-panel {
|
|
|
background: #fff;
|