yangg 5 өдөр өмнө
parent
commit
3bce3b6af9

+ 340 - 19
src/views/position/detail/index.vue

@@ -387,8 +387,8 @@
     >
       <div class="process-dialog-content">
         <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 < recruitmentProcess.length - 1">
             <el-button type="text" @click="showStepOptions(index, $event)">
               <el-icon><Plus /></el-icon>
             </el-button>
@@ -408,7 +408,7 @@
                 type="info" 
                 size="small" 
                 plain 
-                @click="editAIVideo(step)"
+                @click="editProcessStep(step, index)"
               >
                 修改
               </el-button>
@@ -2064,8 +2064,8 @@
           <!-- 分页 -->
           <div class="pagination-container">
             <el-pagination
-              v-model:current-page="questionList.page"
-              v-model:page-size="questionList.pageSize"
+              v-model:current-page="questionListQuery.page"
+              v-model:page-size="questionListQuery.pageSize"
               :total="totalQuestions2"
               :page-sizes="[10, 20, 50, 100]"
               layout="total, sizes, prev, pager, next"
@@ -2083,6 +2083,130 @@
         </span>
       </template>
     </el-dialog>
+
+    <!-- 候选人提问环节配置弹窗 -->
+    <el-dialog
+      :title="editingCandidateQuestionsIndex >= 0 ? '编辑候选人提问环节' : '配置候选人提问环节'"
+      v-model="showCandidateQuestionsDialog"
+      width="600px"
+      :close-on-click-modal="false"
+      class="candidate-questions-dialog"
+    >
+      <div class="candidate-questions-form">
+        <div class="form-item">
+          <div class="form-label">
+            环节名称<span class="required">*</span>
+          </div>
+          <el-input 
+            v-model="candidateQuestionsForm.title"
+            placeholder="请输入环节名称"
+            maxlength="30"
+            show-word-limit
+          />
+        </div>
+
+        <div class="form-item">
+          <div class="form-label">
+            提问时长<span class="required">*</span>
+          </div>
+          <el-input-number
+            v-model="candidateQuestionsForm.duration"
+            :min="1"
+            :max="30"
+            :step="1"
+            controls-position="right"
+            style="width: 200px"
+          />
+          <span class="form-unit">分钟</span>
+          <div class="form-tip">候选人可用于提问的总时长</div>
+        </div>
+
+        <!-- <div class="form-item">
+          <div class="form-label">
+            最多提问数量<span class="required">*</span>
+          </div>
+          <el-input-number
+            v-model="candidateQuestionsForm.maxQuestions"
+            :min="1"
+            :max="10"
+            :step="1"
+            controls-position="right"
+            style="width: 200px"
+          />
+          <span class="form-unit">个</span>
+          <div class="form-tip">候选人最多可以提出的问题数量</div>
+        </div> -->
+
+        <div class="form-item">
+          <div class="form-label">环节描述</div>
+          <el-input
+            v-model="candidateQuestionsForm.description"
+            type="textarea"
+            rows="3"
+            placeholder="请输入环节描述"
+            maxlength="200"
+            show-word-limit
+          />
+          <div class="form-tip">向候选人说明此环节的目的和意义</div>
+        </div>
+
+       <!--  <div class="form-item">
+          <div class="form-label">环节设置</div>
+          <div class="form-checkboxes">
+            <el-checkbox v-model="candidateQuestionsForm.isRequired">
+              必须环节(候选人必须完成此环节)
+            </el-checkbox>
+            <el-checkbox v-model="candidateQuestionsForm.allowSkip">
+              允许跳过(候选人可选择跳过此环节)
+            </el-checkbox>
+          </div>
+        </div>
+
+        <div class="form-item">
+          <div class="form-label">提问指引</div>
+          <div class="guidelines-list">
+            <div 
+              v-for="(guideline, index) in candidateQuestionsForm.guidelines" 
+              :key="index"
+              class="guideline-item"
+            >
+              <el-input
+                v-model="candidateQuestionsForm.guidelines[index]"
+                placeholder="请输入提问指引"
+                maxlength="100"
+              >
+                <template #append>
+                  <el-button 
+                    type="danger" 
+                    @click="removeGuideline(index)"
+                    v-if="candidateQuestionsForm.guidelines.length > 1"
+                  >
+                    删除
+                  </el-button>
+                </template>
+              </el-input>
+            </div>
+            <el-button 
+              type="primary" 
+              plain 
+              @click="addGuideline"
+              v-if="candidateQuestionsForm.guidelines.length < 5"
+            >
+              <el-icon><Plus /></el-icon>
+              添加指引
+            </el-button>
+          </div>
+          <div class="form-tip">为候选人提供提问的指导建议,最多5条</div>
+        </div> -->
+      </div>
+      
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="cancelCandidateQuestions">取消</el-button>
+          <el-button type="primary" @click="confirmCandidateQuestions">确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -2193,15 +2317,32 @@ const positionData = reactive({
   status: 0
 });
 
-// 招聘流程数据 - 修改为普通数组而非 ref 对象
+// 招聘流程数据 - 修改为普通数组而非 ref 对象,添加type字段用于重复检查
 const recruitmentProcess = reactive([
-  { id: 5, name: '资料收集', description: '资料收集', active: true },
- /*  { id: 7, name: '常识问题', description: '常识问题', active: true },
-  { id: 6, name: '心理问题', description: '心理问题', active: true }, */
-  { id: 2, name: 'AI考察', description: 'AI考察', active: true },
-  { id: 1, name: '待核验', description: '待核验', active: true },
-  { id: 3, name: '已通过', description: '已通过', active: true },
-  { id: 4, name: '已淘汰', description: '已淘汰', active: false },
+  { id: 6, name: '简历收集', description: '简历收集', active: true, type: 'resume_collection' },
+  { id: 5, name: '资料收集', description: '资料收集', active: true, type: 'data_collection' },
+   { id: 2, name: 'AI考察', description: 'AI考察', active: true, type: 'ai_video' },
+  { id: 7, name: '候选人提问', description: '时长5分钟,最多3个问题', active: true, type: 'candidate_questions',
+    config: {
+      title: '候选人提问',
+      duration: 5,
+      maxQuestions: 3,
+      description: '候选人可以向面试官提问关于公司、职位或团队的问题',
+      isRequired: true,
+      allowSkip: false,
+      guidelines: [
+        '请提出与职位、公司文化或团队相关的问题',
+        '避免询问已在职位描述中明确说明的信息',
+        '可以询问职业发展机会和团队工作方式'
+      ]
+    }
+  },
+ /*  { id: 8, name: '常识问题', description: '常识问题', active: true, type: 'common_sense' },
+  { id: 9, name: '心理问题', description: '心理问题', active: true, type: 'psychological_problem' }, */
+ 
+ /*  { id: 1, name: '待核验', description: '待核验', active: true, type: 'pending_verification' }, */
+  { id: 3, name: '已通过', description: '已通过', active: true, type: 'passed' },
+  { id: 4, name: '已淘汰', description: '已淘汰', active: false, type: 'rejected' },
   
 ]);
 
@@ -2211,10 +2352,11 @@ const showProcessDialog = ref(false);
 // 定义可选的流程步骤类型
 const processStepOptions = [
   { label: 'AI考察', value: 'ai_video' },
-  { label: '视频宣讲', value: 'video_presentation' },
-  { label: 'AI实时对话', value: 'ai_chat' },
+ /*  { label: '视频宣讲', value: 'video_presentation' }, 
+  { label: 'AI实时对话', value: 'ai_chat' },*/
   { label: '资料收集', value: 'data_collection' },
   { label: '简历收集', value: 'resume_collection' },
+  { label: '候选人提问', value: 'candidate_questions' },
   { label: '心理问题', value: 'psychological_problem' },
   { label: '常识问题', value: 'common_sense' },
   /* { label: '代码测试', value: 'code_test' },
@@ -2774,7 +2916,7 @@ const cancelEditProcess = () => {
   showProcessDialog.value = false;
 };
 
-// 删除流程步骤
+// 删除流程步骤 - 删除后允许重新添加相同类型的步骤
 const deleteProcessStep = (index: number) => {
   recruitmentProcess.splice(index, 1);
 };
@@ -2790,7 +2932,8 @@ const addProcessStepAt = (index: number) => {
     id: Date.now(),
     name: '新步骤',
     description: '新步骤',
-    active: true
+    active: true,
+    type: 'custom' // 自定义步骤类型
   };
   
   recruitmentProcess.splice(index, 0, newStep);
@@ -2832,7 +2975,7 @@ const getJobTypeText = (type: number) => {
   return typeMap[type] || '未知';
 };
 
-// 显示选项菜单
+// 显示选项菜单 - 用于在指定位置添加招聘流程步骤
 const showStepOptions = (index: number, event: MouseEvent) => {
   currentAddIndex.value = index;
   showOptionsMenu.value = true;
@@ -2862,6 +3005,25 @@ const showStepOptions = (index: number, event: MouseEvent) => {
 };
 // 修改添加选定类型的步骤方法
 const addSelectedStepType = async (type: string, label: string) => {
+  // 检查是否已存在相同类型的步骤,防止重复添加
+  const existingStep = recruitmentProcess.find(step => {
+    // 根据步骤名称或类型进行判断
+    if (step.type && step.type === type) {
+      return true;
+    }
+    // 对于没有type字段的旧数据,根据name进行判断
+    if (step.name === label) {
+      return true;
+    }
+    return false;
+  });
+  
+  if (existingStep) {
+    ElMessage.warning(`"${label}"步骤已存在,不能重复添加`);
+    showOptionsMenu.value = false;
+    return;
+  }
+  
   if (type === 'ai_video') {
     showAIVideoDialog.value = true;
     showOptionsMenu.value = false;
@@ -2918,12 +3080,20 @@ const addSelectedStepType = async (type: string, label: string) => {
     return;
   }
   
-  // 其他类型的处理保持不变
+  // 添加候选人提问环节的处理
+  if (type === 'candidate_questions') {
+    showCandidateQuestionsDialog.value = true;
+    showOptionsMenu.value = false;
+    return;
+  }
+  
+  // 其他类型的处理保持不变 - 检查流程步骤数量限制
   if (recruitmentProcess.length >= 6) {
     ElMessage.warning('最多只能添加6个流程步骤');
     return;
   }
   
+  // 创建新的招聘流程步骤,包含type字段用于重复检查
   const newStep = {
     id: Date.now(),
     name: label,
@@ -2932,6 +3102,7 @@ const addSelectedStepType = async (type: string, label: string) => {
     type: type
   };
   
+  // 在指定位置插入新步骤
   recruitmentProcess.splice(currentAddIndex.value, 0, newStep);
   showOptionsMenu.value = false;
 };
@@ -3239,6 +3410,82 @@ const cancelAIChatConfig = () => {
   showAIChatDialog.value = false;
 };
 
+// 候选人提问环节相关方法 - 用于配置候选人提问环节
+const confirmCandidateQuestions = () => {
+  // 验证表单数据
+  if (!candidateQuestionsForm.title.trim()) {
+    ElMessage.warning('请输入环节名称');
+    return;
+  }
+  
+  if (candidateQuestionsForm.duration <= 0) {
+    ElMessage.warning('提问时长必须大于0分钟');
+    return;
+  }
+  
+  if (candidateQuestionsForm.maxQuestions <= 0) {
+    ElMessage.warning('最多提问数量必须大于0');
+    return;
+  }
+  
+  // 判断是编辑模式还是新增模式
+  if (editingCandidateQuestionsIndex.value >= 0) {
+    // 编辑模式:更新现有步骤
+    const existingStep = recruitmentProcess[editingCandidateQuestionsIndex.value];
+    existingStep.name = candidateQuestionsForm.title;
+    existingStep.description = `时长${candidateQuestionsForm.duration}分钟,最多${candidateQuestionsForm.maxQuestions}个问题`;
+    existingStep.config = { ...candidateQuestionsForm };
+    
+    ElMessage.success('候选人提问环节已更新');
+  } else {
+    // 新增模式:创建新的候选人提问步骤
+    const newStep = {
+      id: Date.now(),
+      name: candidateQuestionsForm.title,
+      description: `时长${candidateQuestionsForm.duration}分钟,最多${candidateQuestionsForm.maxQuestions}个问题`,
+      active: true,
+      type: 'candidate_questions',
+      config: {
+        ...candidateQuestionsForm
+      }
+    };
+    
+    // 添加到流程中
+    recruitmentProcess.splice(currentAddIndex.value, 0, newStep);
+    
+    ElMessage.success('候选人提问环节已添加');
+  }
+  
+  // 关闭弹窗
+  showCandidateQuestionsDialog.value = false;
+  
+  // 重置表单为默认值
+  resetCandidateQuestionsForm();
+};
+
+const cancelCandidateQuestions = () => {
+  showCandidateQuestionsDialog.value = false;
+  resetCandidateQuestionsForm();
+};
+
+// 重置候选人提问表单为默认值
+const resetCandidateQuestionsForm = () => {
+  editingCandidateQuestionsIndex.value = -1; // 重置编辑索引
+  Object.assign(candidateQuestionsForm, {
+    title: '候选人提问',
+    duration: 5,
+    maxQuestions: 3,
+    description: '候选人可以向面试官提问关于公司、职位或团队的问题',
+    isRequired: true,
+    allowSkip: false,
+    guidelines: [
+      '请提出与职位、公司文化或团队相关的问题',
+      '避免询问已在职位描述中明确说明的信息',
+      '可以询问职业发展机会和团队工作方式'
+    ]
+  });
+};
+
 // 添加问题列表数据
 const chatQuestions = ref<ChatQuestion[]>([]);
 
@@ -5559,6 +5806,80 @@ const handleQuestionTypeChange = (value: string) => {
 // 添加状态变量
 const competencyLoading = ref(false);
 
+// 候选人提问环节相关变量 - 用于候选人向面试官提问的环节配置
+const showCandidateQuestionsDialog = ref(false); // 控制候选人提问弹窗显示
+const editingCandidateQuestionsIndex = ref(-1); // 编辑候选人提问环节的索引
+const candidateQuestionsForm = reactive({
+  title: '候选人提问', // 环节名称
+  duration: 5, // 提问时长(分钟)
+  maxQuestions: 3, // 最多提问数量
+  description: '候选人可以向面试官提问关于公司、职位或团队的问题', // 环节描述
+  isRequired: true, // 是否必须环节
+  allowSkip: false, // 是否允许跳过
+  guidelines: [ // 提问指引
+    '请提出与职位、公司文化或团队相关的问题',
+    '避免询问已在职位描述中明确说明的信息',
+    '可以询问职业发展机会和团队工作方式'
+  ]
+});
+
+const removeGuideline = (index: number) => {
+  candidateQuestionsForm.guidelines.splice(index, 1);
+};
+
+const addGuideline = () => {
+  candidateQuestionsForm.guidelines.push('');
+};
+
+// 编辑候选人提问环节
+const editCandidateQuestions = (step: any, index: number) => {
+  editingCandidateQuestionsIndex.value = index;
+  
+  // 如果步骤有配置信息,加载到表单中
+  if (step.config) {
+    Object.assign(candidateQuestionsForm, step.config);
+  } else {
+    // 如果没有配置信息,从步骤的基本信息推断
+    candidateQuestionsForm.title = step.name;
+    candidateQuestionsForm.description = step.description || '候选人可以向面试官提问关于公司、职位或团队的问题';
+    candidateQuestionsForm.duration = 5;
+    candidateQuestionsForm.maxQuestions = 3;
+    candidateQuestionsForm.isRequired = step.active;
+    candidateQuestionsForm.allowSkip = false;
+    candidateQuestionsForm.guidelines = [
+      '请提出与职位、公司文化或团队相关的问题',
+      '避免询问已在职位描述中明确说明的信息',
+      '可以询问职业发展机会和团队工作方式'
+    ];
+  }
+  
+  showCandidateQuestionsDialog.value = true;
+};
+
+// 统一的编辑流程步骤方法
+const editProcessStep = (step: any, index: number) => {
+  switch (step.type) {
+    case 'ai_video':
+      editAIVideo(step);
+      break;
+    case 'candidate_questions':
+      editCandidateQuestions(step, index);
+      break;
+    case 'data_collection':
+      // TODO: 添加资料收集编辑功能
+      editAIVideo(step);
+      /* ElMessage.info('资料收集编辑功能开发中'); */
+      break;
+    case 'resume_collection':
+      // TODO: 添加简历收集编辑功能
+      ElMessage.info('简历收集编辑功能开发中');
+      break;
+    default:
+      ElMessage.info(`${step.name}编辑功能开发中`);
+      break;
+  }
+};
+
 </script>
 
 <style scoped>