浏览代码

修改内容

yangg 6 天之前
父节点
当前提交
7745b6ed19

+ 13 - 3
src/views/JobApplication/list/crud.tsx

@@ -528,15 +528,25 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 				},
 				hire_recommendation: {
 					title: '录用建议',
-					type: 'textarea',
+					type: 'dict-select',
 					search: {
 						show: true,
+						type: 'dict-select',
+						
 						component: {
-							placeholder: '请输入录用建议',
+							placeholder: '请选择录用建议',
+							clearable: true,
 						},
 						size: 'small',
 						col:{ span:3},
-					},
+					},dict: dict({
+						data: [
+							{ value: '谨慎录用', label: '谨慎录用' },
+							{ value: '推荐录用', label: '推荐录用' },
+							{ value: '有条件录用', label: '有条件录用' },
+							{ value: '不推荐录用', label: '不推荐录用' },
+						],
+					}),
 					column: {
 						minWidth: 150,
 						showOverflowTooltip: true,

+ 23 - 1
src/views/position/Jobcompetency/crud.tsx

@@ -139,7 +139,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show: true,
 					},
 				},
-				name: {
+				keyword: {
 					title: '标签名称',
 					search: {
 						show: true,
@@ -150,6 +150,28 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						col:{ span:3},
 					},
 					type: 'input',
+					column: {
+						show: false,
+						minWidth: 120,
+					},
+					form: {
+						show: false,
+						rules: [
+							{ required: true, message: '标签名称为必填项' },
+						],
+					},
+				},
+				name: {
+					title: '标签名称',
+					search: {
+						show: false,
+						component: {
+							placeholder: '请输入标签名称',
+						},
+						size: 'small',
+						col:{ span:3},
+					},
+					type: 'input',
 					column: {
 						minWidth: 120,
 					},

+ 124 - 0
src/views/questionBank/list/components/BlankAnswerEditor.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="blank-answer-editor">
+    <el-card>
+      <div class="blank-header">
+        <span style="width: 80px;">填空序号</span>
+        <span style="flex: 1;">正确答案</span>
+        <span style="width: 120px; text-align: center;">分值</span>
+      </div>
+      <div 
+        v-for="(blank, index) in blankAnswers" 
+        :key="index" 
+        class="blank-item"
+      >
+        <span style="width: 80px;">{{ blank.blank_index }}</span>
+        <el-input 
+          v-model="blank.correct_answer" 
+          placeholder="请输入正确答案" 
+          style="flex: 1; margin: 0 10px;"
+        />
+        <el-input-number
+          v-model="blank.score"
+          :min="0"
+          :max="100"
+          style="width: 120px;"
+          placeholder="分值"
+        />
+      </div>
+      <div class="blank-tip">
+        提示:在题目中使用"___"(三个下划线)表示填空位置,系统会自动识别填空数量
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, computed } from 'vue';
+
+interface BlankAnswer {
+  blank_index: number;
+  correct_answer: string;
+  score: number;
+}
+
+interface Props {
+  modelValue: BlankAnswer[];
+  question: string;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: BlankAnswer[]): void;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits<Emits>();
+
+const blankAnswers = ref<BlankAnswer[]>([...props.modelValue]);
+
+// 计算题目中的填空数量
+const blankCount = computed(() => {
+  return (props.question || '').split('___').length - 1;
+});
+
+// 监听数据变化
+watch(() => blankAnswers.value, (newVal) => {
+  emit('update:modelValue', newVal);
+}, { deep: true });
+
+watch(() => props.modelValue, (newVal) => {
+  blankAnswers.value = [...newVal];
+}, { deep: true });
+
+// 监听题目变化,自动调整填空数量
+watch(() => props.question, (newQuestion) => {
+  const newBlankCount = (newQuestion || '').split('___').length - 1;
+  
+  if (newBlankCount > 0 && blankAnswers.value.length !== newBlankCount) {
+    blankAnswers.value = Array.from({ length: newBlankCount }, (_, i) => ({
+      blank_index: i + 1,
+      correct_answer: blankAnswers.value[i]?.correct_answer || '',
+      score: blankAnswers.value[i]?.score || 5
+    }));
+  }
+});
+
+// 初始化填空模板
+if (blankAnswers.value.length === 0) {
+  blankAnswers.value = [{ blank_index: 1, correct_answer: '', score: 5 }];
+}
+</script>
+
+<style scoped>
+.blank-answer-editor {
+  width: 100%;
+}
+
+.blank-header {
+  display: flex;
+  margin-bottom: 10px;
+  font-weight: bold;
+  background-color: #f5f7fa;
+  padding: 8px;
+  border-radius: 4px;
+}
+
+.blank-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  padding: 8px;
+}
+
+.blank-tip {
+  color: #666;
+  font-size: 12px;
+  margin-top: 10px;
+  padding: 8px;
+  background-color: #f0f9ff;
+  border-radius: 4px;
+  border-left: 4px solid #3b82f6;
+}
+</style>
+

+ 436 - 0
src/views/questionBank/list/components/QuestionBankForm.vue

@@ -0,0 +1,436 @@
+<template>
+  <div class="question-bank-form">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="120px"
+      @submit.prevent="handleSubmit"
+    >
+      <!-- 题目内容 -->
+      <el-form-item label="题目内容" prop="question">
+        <el-input
+          v-model="formData.question"
+          type="textarea"
+          :rows="3"
+          placeholder="请输入题目内容"
+        />
+      </el-form-item>
+
+      <!-- 题目形式 -->
+      <el-form-item label="题目形式" prop="question_form">
+        <el-select
+          v-model="formData.question_form"
+          placeholder="请选择题目形式"
+          @change="handleQuestionFormChange"
+        >
+          <el-option
+            v-for="item in questionFormOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
+
+      <!-- 胜任力标签 -->
+      <el-form-item label="胜任力标签">
+        <el-select
+          v-model="formData.competency_tags"
+          multiple
+          filterable
+          placeholder="请选择胜任力标签"
+          @change="handleCompetencyTagsChange"
+        >
+          <el-option
+            v-for="item in competencyTagsOptions"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <!-- 胜任力配置表格 -->
+      <el-form-item 
+        v-if="formData.competency_tags && formData.competency_tags.length > 0"
+        label="胜任力配置"
+      >
+        <el-card>
+          <el-table :data="formData.competency_tags_config" border>
+            <el-table-column label="标签" prop="competency_name" width="200" />
+            <el-table-column label="权重" width="150">
+              <template #default="{ row }">
+                <div style="display: flex; align-items: center;">
+                  <el-input-number
+                    v-model="row.weight"
+                    :min="0"
+                    :max="100"
+                    :step="1"
+                    :precision="0"
+                    :controls="false"
+                    style="width: calc(100% - 20px)"
+                  />
+                  <span style="margin-left: 4px;">%</span>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="级别" width="100">
+              <template #default="{ row }">
+                <el-input-number
+                  v-model="row.importance"
+                  :min="1"
+                  :controls="false"
+                />
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-form-item>
+
+      <!-- 适用职位 -->
+      <el-form-item label="适用职位">
+        <el-select
+          v-model="formData.position_types"
+          multiple
+          filterable
+          placeholder="请选择适用职位类型"
+        >
+          <el-option
+            v-for="item in positionOptions"
+            :key="item.id"
+            :label="item.title"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <!-- 分类 -->
+      <el-form-item label="分类">
+        <el-select
+          v-model="formData.category_id"
+          placeholder="请选择题目分类"
+        >
+          <el-option
+            v-for="item in categoryOptions"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <!-- 标签 -->
+      <el-form-item label="标签">
+        <el-select
+          v-model="formData.tags"
+          multiple
+          filterable
+          placeholder="请选择标签"
+        >
+          <el-option
+            v-for="item in tagOptions"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <!-- 建议时长 -->
+      <el-form-item label="建议时长(秒)">
+        <el-input-number
+          v-model="formData.recommended_duration"
+          :min="10"
+          :max="600"
+          :step="10"
+        />
+      </el-form-item>
+
+      <!-- 状态 -->
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="formData.status" placeholder="请选择状态">
+          <el-option label="停用" :value="0" />
+          <el-option label="启用" :value="1" />
+        </el-select>
+      </el-form-item>
+
+      <!-- 红线问题 (仅单选题和多选题显示) -->
+      <el-form-item 
+        v-if="formData.question_form === 1 || formData.question_form === 2"
+        label="红线问题"
+      >
+        <el-select v-model="formData.is_required_correct" placeholder="请选择是否为红线问题">
+          <el-option label="否" :value="false" />
+          <el-option label="是" :value="true" />
+        </el-select>
+      </el-form-item>
+
+      <!-- 系统题目 -->
+      <el-form-item label="系统题目">
+        <el-select v-model="formData.is_system" placeholder="请选择是否为系统题目">
+          <el-option label="否" :value="false" />
+          <el-option label="是" :value="true" />
+        </el-select>
+      </el-form-item>
+
+      <!-- 排序 -->
+      <el-form-item label="排序">
+        <el-input-number
+          v-model="formData.sort"
+          :min="1"
+          :max="999"
+        />
+      </el-form-item>
+
+      <!-- 选项列表 (单选题、多选题、心理评估题) -->
+      <el-form-item 
+        v-if="[1, 2, 4].includes(formData.question_form)"
+        label="选项列表"
+      >
+        <QuestionOptionsEditor
+          v-model="formData.options"
+          :question-form="formData.question_form"
+        />
+      </el-form-item>
+
+      <!-- 评分标准 -->
+      <el-form-item 
+        v-if="[4, 6].includes(formData.question_form)"
+        label="评分标准"
+      >
+        <el-input
+          v-model="formData.scoring_reference"
+          type="textarea"
+          :rows="4"
+          placeholder="请输入评分标准说明"
+        />
+      </el-form-item>
+
+      <!-- 填空答案模板 -->
+      <el-form-item 
+        v-if="formData.question_form === 6"
+        label="填空答案模板"
+      >
+        <BlankAnswerEditor
+          v-model="formData.blank_answer_template"
+          :question="formData.question"
+        />
+      </el-form-item>
+
+      <!-- 操作按钮 -->
+      <el-form-item>
+        <el-button type="primary" @click="handleSubmit">确定</el-button>
+        <el-button @click="handleCancel">取消</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, watch, onMounted } from 'vue';
+import type { FormInstance } from 'element-plus';
+import * as api from '../api';
+import QuestionOptionsEditor from './QuestionOptionsEditor.vue';
+import BlankAnswerEditor from './BlankAnswerEditor.vue';
+
+interface Props {
+  modelValue?: any;
+  isEdit?: boolean;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: any): void;
+  (e: 'submit', value: any): void;
+  (e: 'cancel'): void;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => ({}),
+  isEdit: false
+});
+
+const emit = defineEmits<Emits>();
+
+const formRef = ref<FormInstance>();
+
+// 表单数据
+const formData = reactive({
+  question: '',
+  question_form: null,
+  competency_tags: [],
+  competency_tags_config: [],
+  position_types: [],
+  category_id: null,
+  tags: [],
+  recommended_duration: 60,
+  status: 1,
+  is_required_correct: false,
+  is_system: false,
+  sort: 1,
+  options: [],
+  scoring_reference: '',
+  blank_answer_template: [],
+  ...props.modelValue
+});
+
+// 表单验证规则
+const formRules = {
+  question: [{ required: true, message: '题目内容必填', trigger: 'blur' }],
+  question_form: [{ required: true, message: '题目形式必填', trigger: 'change' }],
+  status: [{ required: true, message: '状态必填', trigger: 'change' }]
+};
+
+// 选项数据
+const questionFormOptions = ref([]);
+const competencyTagsOptions = ref([]);
+const positionOptions = ref([]);
+const categoryOptions = ref([]);
+const tagOptions = ref([]);
+
+// 监听数据变化
+watch(() => formData, (newVal) => {
+  emit('update:modelValue', newVal);
+}, { deep: true });
+
+watch(() => props.modelValue, (newVal) => {
+  Object.assign(formData, newVal);
+}, { deep: true });
+
+// 处理题目形式变化
+const handleQuestionFormChange = (value: number) => {
+  if (value === 1 || value === 2) {
+    // 单选题或多选题
+    formData.options = [
+      { option_text: '', is_correct: false, sort: 1 },
+      { option_text: '', is_correct: false, sort: 2 }
+    ];
+  } else if (value === 4) {
+    // 心理评估题
+    formData.options = [
+      { option_text: '无', score: 0, sort: 1 },
+      { option_text: '轻度', score: 1, sort: 2 },
+      { option_text: '中度', score: 2, sort: 3 },
+      { option_text: '偏重', score: 3, sort: 4 },
+      { option_text: '严重', score: 4, sort: 5 }
+    ];
+    if (!formData.scoring_reference) {
+      formData.scoring_reference = '该评分反映了症状表现的频率与严重程度。';
+    }
+  } else if (value === 6) {
+    // 填空题
+    formData.blank_answer_template = [
+      { blank_index: 1, correct_answer: '', score: 5 }
+    ];
+    if (!formData.scoring_reference) {
+      formData.scoring_reference = '根据答案的准确性评分';
+    }
+  } else {
+    // 开放题
+    formData.options = [];
+  }
+};
+
+// 处理胜任力标签变化
+const handleCompetencyTagsChange = (value: number[]) => {
+  if (!formData.competency_tags_config) {
+    formData.competency_tags_config = [];
+  }
+
+  const existingConfigIds = formData.competency_tags_config.map((config: any) => config.id);
+
+  // 添加新选择的标签配置
+  value.forEach((tagId: number) => {
+    if (!existingConfigIds.includes(tagId)) {
+      const tagInfo = competencyTagsOptions.value.find((tag: any) => tag.id === tagId);
+      if (tagInfo) {
+        formData.competency_tags_config.push({
+          id: tagId,
+          competency_name: tagInfo.name,
+          weight: 100,
+          importance: 1,
+          remark: ''
+        });
+      }
+    }
+  });
+
+  // 移除不再选中的标签配置
+  formData.competency_tags_config = formData.competency_tags_config.filter(
+    (config: any) => value.includes(config.id)
+  );
+};
+
+// 获取选项数据
+const fetchOptions = async () => {
+  try {
+    // 获取题目形式选项
+    const questionFormRes = await api.GetQuestionTypeList();
+    questionFormOptions.value = questionFormRes.data.question_forms || [];
+
+    // 获取胜任力标签选项
+    const competencyRes = await api.GetCompetencyList({page:1, limit:100, tenant_id:1});
+    competencyTagsOptions.value = competencyRes.data.items || [];
+
+    // 获取职位选项
+    const positionRes = await api.GetPositionList({page:1, limit:1000, tenant_id:1});
+    positionOptions.value = positionRes.data || [];
+
+    // 获取分类选项
+    const categoryRes = await api.GetcategoryList({page:1, limit:1000, tenant_id:1});
+    categoryOptions.value = categoryRes.data.items || [];
+
+    // 获取标签选项
+    const tagRes = await api.GetTagList({page:1, limit:1000, tenant_id:1});
+    tagOptions.value = tagRes.data.items || [];
+  } catch (error) {
+    console.error('获取选项数据失败:', error);
+  }
+};
+
+// 表单提交
+const handleSubmit = async () => {
+  try {
+    await formRef.value?.validate();
+    
+    // 处理胜任力标签配置
+    if (formData.competency_tags && formData.competency_tags.length > 0 && formData.competency_tags_config) {
+      formData.competency_tags = formData.competency_tags_config.map((config: any) => ({
+        id: config.id,
+        weight: config.weight || 100,
+        importance: config.importance || 1,
+        remark: config.remark || ''
+      }));
+    } else {
+      formData.competency_tags_config = [];
+    }
+    
+    emit('submit', formData);
+  } catch (error) {
+    console.error('表单验证失败:', error);
+  }
+};
+
+// 取消操作
+const handleCancel = () => {
+  emit('cancel');
+};
+
+// 组件挂载时获取选项数据
+onMounted(() => {
+  fetchOptions();
+});
+
+// 暴露方法
+defineExpose({
+  validate: () => formRef.value?.validate(),
+  resetFields: () => formRef.value?.resetFields(),
+  getFormData: () => formData
+});
+</script>
+
+<style scoped>
+.question-bank-form {
+  padding: 20px;
+}
+</style>

+ 195 - 0
src/views/questionBank/list/components/QuestionOptionsEditor.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="question-options-editor">
+    <el-card>
+      <!-- 心理评估题的选项渲染 -->
+      <div v-if="questionForm === 4">
+        <div class="option-header">
+          <span style="flex: 1;">选项内容</span>
+          <span style="width: 120px; text-align: center;">分值</span>
+        </div>
+        <div 
+          v-for="(option, index) in options" 
+          :key="index" 
+          class="option-item"
+        >
+          <el-input 
+            v-model="option.option_text" 
+            placeholder="请输入选项内容" 
+            style="flex: 1; margin-right: 10px;"
+          />
+          <el-input-number
+            v-model="option.score"
+            :min="0"
+            :max="10"
+            style="width: 120px;"
+            placeholder="分值"
+          />
+        </div>
+      </div>
+
+      <!-- 单选题和多选题的选项渲染 -->
+      <div v-else>
+        <div class="option-header">
+          <span style="flex: 1;">选项内容</span>
+          <span style="width: 80px; text-align: center;">是否正确</span>
+        </div>
+        <div 
+          v-for="(option, index) in options" 
+          :key="index" 
+          class="option-item"
+        >
+          <el-input 
+            v-model="option.option_text" 
+            placeholder="请输入选项内容" 
+            style="flex: 1; margin-right: 10px;"
+          />
+          <el-tooltip 
+            content="设置为正确答案" 
+            placement="top" 
+            effect="light"
+          >
+            <div 
+              @click="handleCorrectAnswer(index)"
+              style="cursor: pointer; width: 80px; text-align: center;"
+            >
+              <el-radio 
+                v-if="questionForm === 1"
+                :model-value="option.is_correct"
+                :label="true"
+              />
+              <el-checkbox 
+                v-else
+                :model-value="option.is_correct"
+              />
+            </div>
+          </el-tooltip>
+        </div>
+      </div>
+
+      <!-- 操作按钮 -->
+      <div class="option-actions">
+        <el-button 
+          type="primary" 
+          @click="addOption"
+        >
+          添加选项
+        </el-button>
+        
+        <el-button 
+          v-if="options.length > 2"
+          type="danger" 
+          @click="removeLastOption"
+        >
+          删除最后一项
+        </el-button>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+
+interface Option {
+  option_text: string;
+  is_correct?: boolean;
+  score?: number;
+  sort: number;
+}
+
+interface Props {
+  modelValue: Option[];
+  questionForm: number;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: Option[]): void;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits<Emits>();
+
+const options = ref<Option[]>([...props.modelValue]);
+
+// 监听数据变化
+watch(() => options.value, (newVal) => {
+  emit('update:modelValue', newVal);
+}, { deep: true });
+
+watch(() => props.modelValue, (newVal) => {
+  options.value = [...newVal];
+}, { deep: true });
+
+// 处理正确答案设置
+const handleCorrectAnswer = (index: number) => {
+  if (props.questionForm === 1) {
+    // 单选题:只能有一个正确答案
+    options.value.forEach((item, idx) => {
+      item.is_correct = (idx === index);
+    });
+  } else {
+    // 多选题:可以有多个正确答案
+    options.value[index].is_correct = !options.value[index].is_correct;
+  }
+};
+
+// 添加选项
+const addOption = () => {
+  const sort = options.value.length > 0 ? 
+    Math.max(...options.value.map(o => o.sort || 0)) + 1 : 1;
+  
+  if (props.questionForm === 4) {
+    // 心理评估题
+    options.value.push({ 
+      option_text: '', 
+      score: 0, 
+      sort 
+    });
+  } else {
+    // 单选题和多选题
+    options.value.push({ 
+      option_text: '', 
+      is_correct: false, 
+      sort 
+    });
+  }
+};
+
+// 删除最后一个选项
+const removeLastOption = () => {
+  if (options.value.length > 2) {
+    options.value.pop();
+  }
+};
+</script>
+
+<style scoped>
+.question-options-editor {
+  width: 100%;
+}
+
+.option-header {
+  display: flex;
+  margin-bottom: 10px;
+  font-weight: bold;
+  background-color: #f5f7fa;
+  padding: 8px;
+  border-radius: 4px;
+}
+
+.option-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  padding: 8px;
+}
+
+.option-actions {
+  display: flex;
+  justify-content: space-between;
+  margin-top: 10px;
+}
+</style>
+

+ 167 - 0
src/views/questionBank/list/components/treeList.vue

@@ -0,0 +1,167 @@
+<template>
+    <div class="document-tree-container">
+      <div class="document-tree-header">
+        <span class="document-tree-title">题库归属</span>
+        <el-button type="primary" size="small" @click="handleAddDocument">
+          <el-icon><Plus /></el-icon>添加分类
+        </el-button>
+      </div>
+      <el-tree
+        ref="treeRef"
+        :data="treeData"
+        node-key="id"
+        :props="defaultProps"
+        default-expand-all
+        highlight-current
+        @node-click="handleNodeClick"
+      >
+        <template #default="{ node, data }">
+          <div class="custom-tree-node">
+            <div class="node-label">
+              <el-icon><Folder /></el-icon>
+              <span class="ml-2">{{ data.title }}</span>
+            </div>
+          <!--   <div class="node-actions" v-if="data.id">
+              <el-button type="primary" link size="small" @click.stop="handleEdit(data)">
+                <el-icon><Edit /></el-icon>
+              </el-button>
+              <el-button type="danger" link size="small" @click.stop="handleDelete(node, data)">
+                <el-icon><Delete /></el-icon>
+              </el-button>
+            </div> -->
+          </div>
+        </template>
+      </el-tree>
+    </div>
+  </template>
+  
+  <script lang="ts" setup>
+  import { ref, defineProps, defineEmits } from 'vue';
+  import { Plus, Folder, Edit, Delete } from '@element-plus/icons-vue';
+  
+  const props = defineProps({
+    treeData: {
+      type: Array,
+      default: () => []
+    }
+  });
+  
+  const emit = defineEmits(['treeClick', 'updateDocument', 'deleteDocument']);
+  
+  const treeRef = ref(null);
+  
+  const defaultProps = {
+    children: 'children',
+    label: 'title'
+  };
+  
+  // 处理节点点击
+  const handleNodeClick = (data: any, node: any) => {
+    // 获取当前节点的层级路径
+    let currentNode = node;
+    let firstLevelNode = null;
+    let secondLevelNode = null;
+  
+    // 遍历父节点找到一级和二级节点
+    while (currentNode) {
+      if (currentNode.level === 1) {
+        firstLevelNode = currentNode.data;
+      } else if (currentNode.level === 2) {
+        secondLevelNode = currentNode.data;
+      }
+      currentNode = currentNode.parent;
+    }
+  
+    // 如果当前点击的是一级节点
+    if (node.level === 1) {
+      firstLevelNode = data;
+      secondLevelNode = null;
+    }
+    // 如果当前点击的是二级节点
+    else if (node.level === 2) {
+      secondLevelNode = data;
+    }
+  
+    emit('treeClick', {
+      firstLevel: firstLevelNode,
+      secondLevel: secondLevelNode,
+      currentNode: data
+    });
+  };
+  
+  // 添加文档分类
+  const handleAddDocument = () => {
+    emit('updateDocument', 'add');
+  };
+  
+  // 编辑文档分类
+  const handleEdit = (data: any) => {
+    emit('updateDocument', 'update', data);
+  };
+  
+  // 删除文档分类
+  const handleDelete = (node: any, data: any) => {
+    emit('deleteDocument', data.id, () => {
+      // 回调函数,删除成功后可以执行
+    });
+  };
+  
+  defineExpose({
+    treeRef
+  });
+  </script>
+  
+  <style lang="scss" scoped>
+  .document-tree-container {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+  }
+  
+  .document-tree-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+  }
+  
+  .document-tree-title {
+    font-size: 16px;
+    font-weight: bold;
+  }
+  
+  .custom-tree-node {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding-right: 8px;
+  }
+  
+  .node-label {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    min-width: 0;
+    position: relative;
+    
+    .ml-2 {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      flex: 1;
+    }
+  }
+  
+  
+  .node-actions {
+    display: none;
+  }
+  
+  .custom-tree-node:hover .node-actions {
+    display: flex;
+    overflow: hidden;
+       white-space: nowrap;
+       text-overflow: ellipsis;
+  }
+  </style> 

+ 2 - 1
src/views/questionBank/list/crud.tsx

@@ -280,6 +280,7 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 			},
 			// 添加搜索表单配置
 			search: {
+				col: { span: 3 },
 				show: true,
 				buttons: {
 					search: {
@@ -1138,7 +1139,7 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 				// 添加红线问题字段
 				is_required_correct: {
 					title: '红线问题',
-					search: { show: true,
+					search: { show: false,
 						size: 'small',
 						col:{ span:2}, },
 					type: 'dict-select',

+ 4 - 0
src/views/questionBank/list/index.vue

@@ -1,5 +1,6 @@
 <template>
 	<fs-page>
+		<!-- <TreeList ref="treeListRef" :treeData="treeData" /> -->
 		<fs-crud ref="crudRef" :dialog-width="800"  v-bind="crudBinding"> </fs-crud>
 		<PermissionDrawerCom />
 		<BatchTagsDialog ref="batchTagsDialogRef" :crudExpose="crudExpose" />
@@ -17,6 +18,7 @@ import { RoleMenuBtnStores } from './stores/RoleMenuBtnStores';
 import { RoleMenuFieldStores } from './stores/RoleMenuFieldStores';
 import { RoleUsersStores } from './stores/RoleUsersStores';
 import { successMessage } from '../../../utils/message';
+import TreeList from './components/treeList.vue';
 
 const PermissionDrawerCom = defineAsyncComponent(() => import('./components/RoleDrawer.vue'));
 const BatchTagsDialog = defineAsyncComponent(() => import('./components/BatchTagsDialog.vue'));
@@ -31,6 +33,8 @@ const RoleDrawer = RoleDrawerStores(); // 角色-抽屉
 const RoleMenuBtn = RoleMenuBtnStores(); // 角色-菜单
 const RoleMenuField = RoleMenuFieldStores();// 角色-菜单-字段
 const RoleUsers = RoleUsersStores();// 角色-用户
+const treeListRef = ref();
+const treeData = ref([]);
 const { crudBinding, crudRef, crudExpose } = useFs({
 	createCrudOptions,
 	context: {