Explorar el Código

后台:题目知识点修改

jinxia.mo hace 1 mes
padre
commit
d14702f6ea

+ 15 - 11
back-ui/src/components/Editor/index.vue

@@ -76,6 +76,10 @@ const options = ref({
   bounds: document.body,
   debug: "warn",
   modules: {
+    // 防止自动换行,出现p标签
+    clipboard: {
+      matchVisual: false
+    },
     // 工具栏配置
     toolbar: [
       ["bold", "italic", "underline", "strike"],      // 加粗 斜体 下划线 删除线
@@ -132,12 +136,12 @@ onMounted(() => {
       }
     })
     quill.root.addEventListener('paste', handlePasteCapture, true)
-    
+
     // 监听编辑器内容变化,为所有图片添加跨域和防盗链属性
     quill.on('text-change', () => {
       addImageAttributes(quill.root)
     })
-    
+
     // 初始化时也为已有图片添加属性
     nextTick(() => {
       addImageAttributes(quill.root)
@@ -189,7 +193,7 @@ function handleBeforeUpload(file) {
 // 自定义上传方法 - 通过后端获取签名后上传到OSS
 async function customUpload(options) {
   const file = options.file
-  
+
   try {
     // 1. 从后端获取OSS签名
     const signatureResponse = await request({
@@ -199,16 +203,16 @@ async function customUpload(options) {
         dir: 'questions/images/'
       }
     })
-    
+
     if (signatureResponse.code !== 200 || !signatureResponse.data) {
       throw new Error(signatureResponse.msg || '获取OSS签名失败')
     }
-    
+
     const signature = signatureResponse.data
-    
+
     // 2. 生成文件路径
     const filePath = generateFilePath(file, signature.dir)
-    
+
     // 3. 构建FormData,使用PostObject方式上传
     const formData = new FormData()
     formData.append('key', filePath)
@@ -216,13 +220,13 @@ async function customUpload(options) {
     formData.append('OSSAccessKeyId', signature.accessKeyId)
     formData.append('signature', signature.signature)
     formData.append('file', file)
-    
+
     // 4. 上传到OSS
     const response = await fetch(signature.host, {
       method: 'POST',
       body: formData
     })
-    
+
     if (response.ok || response.status === 204) {
       // 上传成功,返回OSS URL
       const ossUrl = `${signature.host}/${filePath}`
@@ -264,7 +268,7 @@ function handleUploadSuccess(res, file) {
     // 插入图片,使用OSS的完整URL
     const imageUrl = res.url || (import.meta.env.VITE_APP_BASE_API + res.fileName)
     quill.insertEmbed(length, "image", imageUrl)
-    
+
     // 等待图片插入后,为其添加跨域和防盗链属性
     nextTick(() => {
       const images = quill.root.querySelectorAll('img')
@@ -279,7 +283,7 @@ function handleUploadSuccess(res, file) {
         }
       })
     })
-    
+
     // 调整光标到最后
     quill.setSelection(length + 1)
   } else {

+ 278 - 35
back-ui/src/views/learn/questions/index.vue

@@ -89,7 +89,7 @@
         <el-table v-loading="loading" :data="questionsList" @selection-change="handleSelectionChange">
             <el-table-column type="selection" width="55" align="center" fixed="left"/>
             <el-table-column label="题目ID" align="center" prop="id" fixed="left" />
-            <el-table-column label="试题-题干" align="left" prop="title" min-width="350" header-align="center"
+            <el-table-column label="题干" align="left" prop="title" min-width="350" header-align="center"
                              fixed="left">
                 <template #default="scope">
                     <div class="table-cell-content" v-html="formatContentWithImages(scope.row.title)"></div>
@@ -99,12 +99,26 @@
             <el-table-column label="学科" align="center" prop="subjectId">
                 <template #default="scope">
                     <span v-if="scope.row.subjectId">
-                        {{ getSubjectName(scope.row.subjectId) }}
+                        {{ scope.row.subjectId }}-{{ getSubjectName(scope.row.subjectId) }}
                     </span>
                     <span v-else>-</span>
                 </template>
             </el-table-column>
-            <el-table-column label="知识点" align="center" prop="knowledgeId"/>
+            <el-table-column label="知识点" align="center" prop="knowledgeId" show-overflow-tooltip>
+                <template #default="scope">
+                    <el-tooltip v-if="scope.row.knowledgeTrees && scope.row.knowledgeTrees.length > 0"
+                                :content="`${scope.row.knowledgeTrees[0].id}-${scope.row.knowledgeTrees[0].name}`"
+                                placement="top">
+                        <el-link type="primary"
+                                 @click="handleShowKnowledgeTrees(scope.row)"
+                                 :underline="false"
+                                 class="knowledge-link">
+                            {{ scope.row.knowledgeTrees[0].id }}-{{ scope.row.knowledgeTrees[0].name }}
+                        </el-link>
+                    </el-tooltip>
+                    <span v-else>-</span>
+                </template>
+            </el-table-column>
             <el-table-column label="选项" align="center" prop="optionA" show-overflow-tooltip>
                 <template #default="scope">
                     <el-link type="primary" @click="handleShowOptions(scope.row)" :underline="false">
@@ -183,7 +197,7 @@
             <!-- <el-table-column label="试题来源" align="center" prop="fromSite"/> -->
             <!-- <el-table-column label="图片水印" align="center" prop="isSub"/> -->
             <!-- <el-table-column label="常规题" align="center" prop="isNormal"/> -->
-            <el-table-column label="匹配知识点" align="center" prop="isKonw" min-width="120"/>
+<!--            <el-table-column label="匹配知识点" align="center" prop="isKonw" min-width="120"/>-->
             <el-table-column label="tiid" align="center" prop="tiid" min-width="100"/>
             <!-- <el-table-column label="试题题干的md5值" align="center" prop="md5" /> -->
             <!-- <el-table-column label="是否唯一" align="center" prop="isunique"/> -->
@@ -197,11 +211,11 @@
             <!-- <el-table-column label="试题-材料题题干" align="center" prop="title1" min-width="130" show-overflow-tooltip/> -->
             <!-- <el-table-column label="试题解析" align="left" prop="parse0" header-align="center" show-overflow-tooltip/> -->
             <!-- <el-table-column label="answer0" align="center" prop="answer0"/> -->
-            <el-table-column label="是否更新" align="center" prop="isUpdate">
+            <!-- <el-table-column label="是否更新" align="center" prop="isUpdate">
                 <template #default="scope">
                     <dict-tag :options="bool_values" :value="scope.row.isUpdate" />
                 </template>
-            </el-table-column>
+            </el-table-column> -->
             <el-table-column label="包含子题" align="center" prop="isSubType">
                 <template #default="scope">
                     <dict-tag :options="bool_values" :value="scope.row.isSubType" />
@@ -290,18 +304,40 @@
                 <!-- 横线分隔(仅在修改时显示) -->
                 <el-divider v-if="form.id != null"></el-divider>
 
+
+                <!-- <el-row :gutter="20"> -->
+<!--                    <el-col :span="12">-->
+<!--                        <el-form-item label="知识点" prop="knowledgeId">-->
+<!--                            <el-tree-select node-key="id" v-model="form.knowledgeId" :data="knowledgeTreeList" check-strictly-->
+<!--                                            :render-after-expand="false" style="width: 100%"-->
+<!--                                            :props="{ label: 'name', children: 'children' }"-->
+<!--                                            placeholder="请选择知识点"/>-->
+<!--                        </el-form-item>-->
+<!--                    </el-col>-->
+                    
+                <!-- </el-row> -->
                 <!-- 可修改内容区域,两列布局 -->
                 <el-row :gutter="20">
-                    <el-col :span="12">
-                        <el-form-item label="科目" prop="subjectId">
-                            <el-select v-model="form.subjectId" placeholder="请选择科目" style="width: 100%">
-                                <el-option v-for="s in subjectList" :label="s.subjectName" :value="s.subjectId"/>
-                            </el-select>
+<!--                    <el-col :span="12">-->
+<!--                        <el-form-item label="科目" prop="subjectId">-->
+<!--                            <el-select v-model="form.subjectId" placeholder="请选择科目" style="width: 100%">-->
+<!--                                <el-option v-for="s in subjectList" :label="s.subjectName" :value="s.subjectId"/>-->
+<!--                            </el-select>-->
+<!--                        </el-form-item>-->
+<!--                    </el-col>-->
+                    <el-col :span="8">
+                        <el-form-item label="题型" prop="qtpye">
+                            <el-input v-model="form.qtpye" disabled type="text" placeholder="请输入内容"/>
                         </el-form-item>
                     </el-col>
-                    <el-col :span="12">
-                        <el-form-item label="题型" prop="qtpye">
-                            <el-input v-model="form.qtpye" type="text" placeholder="请输入内容"/>
+                    <el-col :span="8">
+                        <el-form-item label="来源" prop="source">
+                            <el-input v-model="form.source" disabled placeholder="请输入来源"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="8">
+                        <el-form-item label="源ID" prop="tiid">
+                            <el-input v-model="form.tiid" placeholder="请输入试题的源ID"/>
                         </el-form-item>
                     </el-col>
                 </el-row>
@@ -379,27 +415,9 @@
                     </el-select>
                 </el-form-item> -->
 
+                
                 <el-row :gutter="20">
-                    <el-col :span="12">
-                        <el-form-item label="知识点" prop="knowledgeId">
-                            <el-tree-select node-key="id" v-model="form.knowledgeId" :data="knowledgeTreeList" check-strictly
-                                            :render-after-expand="false" style="width: 100%"
-                                            :props="{ label: 'name', children: 'children' }"
-                                            placeholder="请选择知识点"/>
-                        </el-form-item>
-                    </el-col>
-                    <el-col :span="12">
-                        <el-form-item label="来源" prop="source">
-                            <el-input v-model="form.source" placeholder="请输入来源"/>
-                        </el-form-item>
-                    </el-col>
-                </el-row>
-                <el-row :gutter="20">
-                    <el-col :span="12">
-                        <el-form-item label="源ID" prop="tiid">
-                            <el-input v-model="form.tiid" placeholder="请输入试题的源ID"/>
-                        </el-form-item>
-                    </el-col>
+
                     <el-col :span="12">
                         <el-form-item label="试题解析" prop="parse">
                             <Editor v-if="!isTextMode" v-model="form.parse" :min-height="120" />
@@ -514,6 +532,77 @@
                 </div>
             </template>
         </el-dialog>
+
+        <!-- 知识点列表管理弹窗 -->
+        <el-dialog v-model="showKnowledgeTreesDialog" title="知识点管理" width="80%" append-to-body class="knowledge-trees-dialog">
+            <el-row :gutter="10" class="mb8">
+                <el-col :span="1.5">
+                    <el-button type="primary" plain icon="Plus" @click="handleAddKnowledgeQuestion">新增</el-button>
+                </el-col>
+            </el-row>
+            <el-table v-loading="knowledgeTreesLoading" :data="currentKnowledgeTreesList">
+                <el-table-column label="题目ID" align="center" prop="questionId" width="100" />
+                <el-table-column label="知识点ID(pid)" align="center" prop="id" width="120">
+                    <template #default="scope">
+                        <span v-if="scope.row.pid !== null && scope.row.pid !== undefined">
+                            {{ scope.row.id }}({{ scope.row.pid }})
+                        </span>
+                        <span v-else>
+                            {{ scope.row.id }}
+                        </span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="知识点" align="center" prop="name" min-width="200" />
+                <el-table-column label="科目" align="center" prop="subjectId" min-width="150">
+                    <template #default="scope">
+                        <span v-if="scope.row.subjectId">{{ scope.row.subjectId }}-{{ getSubjectName(scope.row.subjectId) }}</span>
+                        <span v-else>-</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="100">
+                    <template #default="scope">
+                        <el-button link type="primary" icon="Edit" @click="handleUpdateKnowledgeQuestion(scope.row)">修改</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button @click="showKnowledgeTreesDialog = false">关 闭</el-button>
+                </div>
+            </template>
+        </el-dialog>
+
+        <!-- 知识点关系编辑弹窗 -->
+        <el-dialog :title="knowledgeQuestionTitle" v-model="showKnowledgeQuestionDialog" width="500px" append-to-body>
+            <el-form ref="knowledgeQuestionRef" :model="knowledgeQuestionForm" :rules="knowledgeQuestionRules" label-width="80px">
+                <el-form-item label="题目ID" prop="questionId">
+                    <el-input v-model="knowledgeQuestionForm.questionId" placeholder="请输入题ID" disabled />
+                </el-form-item>
+                <el-form-item label="知识点" prop="knowledgeId">
+                    <el-tree-select
+                        node-key="id"
+                        v-model="knowledgeQuestionForm.knowledgeId"
+                        :data="knowledgeTreeList"
+                        check-strictly
+                        :render-after-expand="false"
+                        style="width: 100%"
+                        :props="{ label: 'name', children: 'children' }"
+                        placeholder="请选择知识点"
+                        filterable
+                        clearable/>
+                </el-form-item>
+                
+                <el-form-item label="顺序" prop="seq">
+                    <el-input v-model="knowledgeQuestionForm.seq" placeholder="请输入顺序" />
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button type="primary" @click="submitKnowledgeQuestionForm">确 定</el-button>
+                    <el-button @click="cancelKnowledgeQuestion">取 消</el-button>
+                </div>
+            </template>
+        </el-dialog>
     </div>
 </template>
 
@@ -521,7 +610,8 @@
 import {listQuestions, getQuestions, delQuestions, addQuestions, updateQuestions, changeQuestionType} from "@/api/learn/questions"
 import {listKnowledgeTree} from "@/api/learn/knowledgeTree"
 import {listAllSubject} from "@/api/dz/subject"
-import {ElMessage} from "element-plus";
+import {listKnowledgeQuestion, getKnowledgeQuestion, delKnowledgeQuestion, addKnowledgeQuestion, updateKnowledgeQuestion} from "@/api/learn/knowledgeQuestion"
+import {ElMessage, ElLoading} from "element-plus";
 import DictTag from '@/components/DictTag/index.vue'
 import Editor from '@/components/Editor/index.vue'
 import { computed } from 'vue'
@@ -549,6 +639,31 @@ const showOptionsDialog = ref(false)
 const currentRow = ref({})
 const isTextMode = ref(false) // 是否为文本模式(false为富文本模式)
 
+// 知识点管理相关
+const showKnowledgeTreesDialog = ref(false)
+const knowledgeTreesLoading = ref(false)
+const currentKnowledgeTreesList = ref([])
+const currentQuestionId = ref(null)
+const showKnowledgeQuestionDialog = ref(false)
+const knowledgeQuestionTitle = ref("")
+const knowledgeQuestionForm = ref({
+    id: null,
+    knowledgeId: null,
+    questionId: null,
+    seq: null
+})
+const knowledgeQuestionRules = {
+    knowledgeId: [
+        { required: true, message: "知识点不能为空", trigger: "change" }
+    ],
+    questionId: [
+        { required: true, message: "题ID不能为空", trigger: "blur" }
+    ],
+    seq: [
+        { required: true, message: "顺序不能为空", trigger: "blur" }
+    ]
+}
+
 const subjectList = ref([])
 const knowledgeTreeList = ref([])
 
@@ -871,6 +986,112 @@ function handleShowOptions(row) {
     showOptionsDialog.value = true
 }
 
+/** 显示知识点列表 */
+function handleShowKnowledgeTrees(row) {
+    currentQuestionId.value = row.id
+    knowledgeTreesLoading.value = true
+    // 如果 row.knowledgeTrees 存在,直接使用;否则通过 questionId 查询知识点关系
+    if (row.knowledgeTrees && row.knowledgeTrees.length > 0) {
+        currentKnowledgeTreesList.value = row.knowledgeTrees.map(kt => ({
+            id: kt.id,
+            name: kt.name,
+            pid: kt.pid,
+            subjectId: kt.subjectId,
+            questionId: kt.questionId,
+            knowledgeQuestionId: kt.knowledgeQuestionId,
+            subjectName: getSubjectName(kt.subjectId)
+        }))
+        knowledgeTreesLoading.value = false
+    } 
+    showKnowledgeTreesDialog.value = true
+}
+
+/** 新增知识点关系 */
+function handleAddKnowledgeQuestion() {
+    resetKnowledgeQuestionForm()
+    knowledgeQuestionForm.value.questionId = currentQuestionId.value
+    knowledgeQuestionTitle.value = "添加知识点关系"
+    showKnowledgeQuestionDialog.value = true
+}
+
+/** 修改知识点关系 */
+function handleUpdateKnowledgeQuestion(row) {
+    // 根据知识点ID和题目ID查找关系
+    const loadingInstance = ElLoading.service({
+        lock: true,
+        text: '加载中...',
+        background: 'rgba(0, 0, 0, 0.7)'
+    })
+    listKnowledgeQuestion({
+        questionId: currentQuestionId.value,
+        knowledgeId: row.id
+    }).then(response => {
+        loadingInstance.close()
+        if (response.rows && response.rows.length > 0) {
+            const relation = response.rows[0]
+            knowledgeQuestionForm.value = {
+                id: relation.id,
+                knowledgeId: relation.knowledgeId,
+                questionId: relation.questionId,
+                seq: relation.seq
+            }
+            knowledgeQuestionTitle.value = "修改知识点关系"
+            showKnowledgeQuestionDialog.value = true
+        } else {
+            ElMessage.warning("未找到对应的知识点关系")
+        }
+    }).catch(() => {
+        loadingInstance.close()
+    })
+}
+
+/** 重置知识点关系表单 */
+function resetKnowledgeQuestionForm() {
+    knowledgeQuestionForm.value = {
+        id: null,
+        knowledgeId: null,
+        questionId: null,
+        seq: null
+    }
+    if (proxy.$refs["knowledgeQuestionRef"]) {
+        proxy.$refs["knowledgeQuestionRef"].resetFields()
+        proxy.$refs["knowledgeQuestionRef"].clearValidate()
+    }
+}
+
+/** 取消知识点关系编辑 */
+function cancelKnowledgeQuestion() {
+    showKnowledgeQuestionDialog.value = false
+    resetKnowledgeQuestionForm()
+}
+
+/** 提交知识点关系表单 */
+function submitKnowledgeQuestionForm() {
+    proxy.$refs["knowledgeQuestionRef"].validate(valid => {
+        if (valid) {
+            if (knowledgeQuestionForm.value.id != null) {
+                updateKnowledgeQuestion(knowledgeQuestionForm.value).then(response => {
+                    proxy.$modal.msgSuccess("修改成功")
+                    showKnowledgeQuestionDialog.value = false
+                    // 刷新知识点列表
+                    handleShowKnowledgeTrees({ id: currentQuestionId.value, knowledgeTrees: [] })
+                    // 刷新题目列表
+                    getList()
+                })
+            } else {
+                addKnowledgeQuestion(knowledgeQuestionForm.value).then(response => {
+                    proxy.$modal.msgSuccess("新增成功")
+                    showKnowledgeQuestionDialog.value = false
+                    // 刷新知识点列表
+                    handleShowKnowledgeTrees({ id: currentQuestionId.value, knowledgeTrees: [] })
+                    // 刷新题目列表
+                    getList()
+                })
+            }
+        }
+    })
+}
+
 /** 获取图片代理URL(如果需要后端代理,可以在这里实现) */
 function getImageProxyUrl(imageUrl) {
     if (!imageUrl) return ''
@@ -1045,4 +1266,26 @@ getList()
 .table-cell-content :deep(img:not([src])) {
     display: none;
 }
+
+.knowledge-link {
+    display: inline-block;
+    max-width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    vertical-align: middle;
+}
+
+/* 知识点管理弹窗样式 */
+.knowledge-trees-dialog :deep(.el-dialog) {
+    height: 75vh;
+    display: flex;
+    flex-direction: column;
+}
+
+.knowledge-trees-dialog :deep(.el-dialog__body) {
+    overflow-x: hidden;
+    flex: 1;
+    overflow-y: auto;
+}
 </style>

+ 19 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnKnowledgeQuestionController.java

@@ -2,6 +2,11 @@ package com.ruoyi.web.controller.learn;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.LearnQuestions;
+import com.ruoyi.learn.service.ILearnKnowledgeTreeService;
+import com.ruoyi.learn.service.ILearnQuestionsService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -23,7 +28,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 
 /**
  * 知识点题关系Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-25
  */
@@ -33,6 +38,10 @@ public class LearnKnowledgeQuestionController extends BaseController
 {
     @Autowired
     private ILearnKnowledgeQuestionService learnKnowledgeQuestionService;
+    @Autowired
+    private ILearnKnowledgeTreeService knowledgeTreeService;
+    @Autowired
+    private ILearnQuestionsService questionsService;
 
     /**
      * 查询知识点题关系列表
@@ -88,6 +97,15 @@ public class LearnKnowledgeQuestionController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody LearnKnowledgeQuestion learnKnowledgeQuestion)
     {
+        //处理科目关联(是否有修改)
+        LearnKnowledgeTree knowledgeTree = knowledgeTreeService.selectLearnKnowledgeTreeById(learnKnowledgeQuestion.getKnowledgeId());
+        if (knowledgeTree != null ) {
+            LearnQuestions q = questionsService.selectLearnQuestionsById(learnKnowledgeQuestion.getQuestionId());
+            if (q != null && !q.getSubjectId().equals(knowledgeTree.getSubjectId())) {
+                q.setSubjectId(knowledgeTree.getSubjectId());
+                questionsService.updateLearnQuestions(q);
+            }
+        }
         return toAjax(learnKnowledgeQuestionService.updateLearnKnowledgeQuestion(learnKnowledgeQuestion));
     }
 

+ 26 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnQuestionsController.java

@@ -19,7 +19,12 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.learn.domain.LearnQuestions;
+import com.ruoyi.learn.domain.LearnKnowledgeQuestion;
+import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.LearnKnowledgeTree2;
 import com.ruoyi.learn.service.ILearnQuestionsService;
+import com.ruoyi.learn.service.ILearnKnowledgeQuestionService;
+import com.ruoyi.learn.service.ILearnKnowledgeTreeService;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.common.core.page.TableDataInfo;
 
@@ -37,6 +42,12 @@ public class LearnQuestionsController extends BaseController
     @Autowired
     private ILearnQuestionsService learnQuestionsService;
 
+    @Autowired
+    private ILearnKnowledgeQuestionService learnKnowledgeQuestionService;
+
+    @Autowired
+    private ILearnKnowledgeTreeService learnKnowledgeTreeService;
+
     /**
      * 查询试题列表
      */
@@ -45,13 +56,27 @@ public class LearnQuestionsController extends BaseController
     @ApiOperation("查询列表")
     public TableDataInfo list(LearnQuestions learnQuestions)
     {
-        startPage();
         if(null != learnQuestions.getTypeId()) {
             QuestionType qt = QuestionType.of(learnQuestions.getTypeId());
             learnQuestions.setQtpye(qt.getTitle());
             learnQuestions.setTypeId(null);
         }
+        startPage();
         List<LearnQuestions> list = learnQuestionsService.selectLearnQuestionsList(learnQuestions);
+        
+        // 查询知识点并设置到题目中
+        if (!list.isEmpty()) {
+            List<Long> questionIds = list.stream().map(LearnQuestions::getId).collect(Collectors.toList());
+            // 直接查询知识点(包含questionId)
+            List<LearnKnowledgeTree2> knowledgeList = learnKnowledgeTreeService.selectLearnKnowledgeByQuestionIds(questionIds);
+            if (!knowledgeList.isEmpty()) {
+                // 按题目ID分组知识点
+                java.util.Map<Long, List<LearnKnowledgeTree2>> questionKnowledgeMap = knowledgeList.stream()
+                    .collect(Collectors.groupingBy(LearnKnowledgeTree2::getQuestionId, Collectors.toList()));
+                // 设置到每个题目
+                list.forEach(q -> q.setKnowledgeTrees(questionKnowledgeMap.get(q.getId())));
+            }
+        }
         return getDataTable(list);
     }
 

+ 18 - 0
ie-system/src/main/java/com/ruoyi/learn/domain/LearnKnowledgeTree2.java

@@ -0,0 +1,18 @@
+package com.ruoyi.learn.domain;
+
+import lombok.Data;
+
+/**
+ * 知识点树扩展对象(包含题目ID)
+ *
+ * @author ruoyi
+ */
+@Data
+public class LearnKnowledgeTree2 extends LearnKnowledgeTree
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 题目ID */
+    private Long questionId;
+    private Long knowledgeQuestionId;
+}

+ 10 - 0
ie-system/src/main/java/com/ruoyi/learn/domain/LearnQuestions.java

@@ -1,6 +1,8 @@
 package com.ruoyi.learn.domain;
 
 import java.math.BigDecimal;
+import java.util.List;
+
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -99,6 +101,7 @@ public class LearnQuestions extends BaseEntity
     @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
     private String knowledges;
 
+
     /** 试题区域 */
     @Excel(name = "试题区域")
     private String area;
@@ -197,7 +200,14 @@ public class LearnQuestions extends BaseEntity
     boolean collect;
 
     private Integer subCnt;
+    private List<LearnKnowledgeTree2> knowledgeTrees;
+    public List<LearnKnowledgeTree2> getKnowledgeTrees() {
+        return knowledgeTrees;
+    }
 
+    public void setKnowledgeTrees(List<LearnKnowledgeTree2> knowledgeTrees) {
+        this.knowledgeTrees = knowledgeTrees;
+    }
 
     public Integer getSubCnt() {
         return subCnt;

+ 9 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnKnowledgeQuestionMapper.java

@@ -2,6 +2,7 @@ package com.ruoyi.learn.mapper;
 
 import java.util.List;
 import com.ruoyi.learn.domain.LearnKnowledgeQuestion;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 知识点题关系Mapper接口
@@ -58,4 +59,12 @@ public interface LearnKnowledgeQuestionMapper
      * @return 结果
      */
     public int deleteLearnKnowledgeQuestionByIds(Long[] ids);
+
+    /**
+     * 根据题目ID列表查询知识点题关系
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点题关系集合
+     */
+    public List<LearnKnowledgeQuestion> selectByQuestionIds(@Param("questionIds") List<Long> questionIds);
 }

+ 17 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnKnowledgeTreeMapper.java

@@ -3,6 +3,7 @@ package com.ruoyi.learn.mapper;
 import java.util.Collection;
 import java.util.List;
 import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.LearnKnowledgeTree2;
 import org.apache.ibatis.annotations.Param;
 
 /**
@@ -62,4 +63,20 @@ public interface LearnKnowledgeTreeMapper
      * @return 结果
      */
     public int deleteLearnKnowledgeTreeByIds(Long[] ids);
+
+    /**
+     * 根据题目ID列表查询知识点树
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByQuestionIds(@Param("questionIds") List<Long> questionIds);
+
+    /**
+     * 根据知识点ID列表查询知识点树
+     * 
+     * @param knowledgeIds 知识点ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByKnowledgeIds(@Param("knowledgeIds") List<Long> knowledgeIds);
 }

+ 8 - 0
ie-system/src/main/java/com/ruoyi/learn/service/ILearnKnowledgeQuestionService.java

@@ -58,4 +58,12 @@ public interface ILearnKnowledgeQuestionService
      * @return 结果
      */
     public int deleteLearnKnowledgeQuestionById(Long id);
+
+    /**
+     * 根据题目ID列表查询知识点题关系
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点题关系集合
+     */
+    public List<LearnKnowledgeQuestion> selectByQuestionIds(List<Long> questionIds);
 }

+ 17 - 0
ie-system/src/main/java/com/ruoyi/learn/service/ILearnKnowledgeTreeService.java

@@ -2,6 +2,7 @@ package com.ruoyi.learn.service;
 
 import java.util.List;
 import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.LearnKnowledgeTree2;
 
 /**
  * 知识点树Service接口
@@ -58,4 +59,20 @@ public interface ILearnKnowledgeTreeService
      * @return 结果
      */
     public int deleteLearnKnowledgeTreeById(Long id);
+
+    /**
+     * 根据题目ID列表查询知识点树
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByQuestionIds(List<Long> questionIds);
+
+    /**
+     * 根据知识点ID列表查询知识点树
+     * 
+     * @param knowledgeIds 知识点ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByKnowledgeIds(List<Long> knowledgeIds);
 }

+ 12 - 0
ie-system/src/main/java/com/ruoyi/learn/service/impl/LearnKnowledgeQuestionServiceImpl.java

@@ -92,4 +92,16 @@ public class LearnKnowledgeQuestionServiceImpl implements ILearnKnowledgeQuestio
     {
         return learnKnowledgeQuestionMapper.deleteLearnKnowledgeQuestionById(id);
     }
+
+    /**
+     * 根据题目ID列表查询知识点题关系
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点题关系集合
+     */
+    @Override
+    public List<LearnKnowledgeQuestion> selectByQuestionIds(List<Long> questionIds)
+    {
+        return learnKnowledgeQuestionMapper.selectByQuestionIds(questionIds);
+    }
 }

+ 25 - 0
ie-system/src/main/java/com/ruoyi/learn/service/impl/LearnKnowledgeTreeServiceImpl.java

@@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper;
 import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.LearnKnowledgeTree2;
 import com.ruoyi.learn.service.ILearnKnowledgeTreeService;
 
 /**
@@ -90,4 +91,28 @@ public class LearnKnowledgeTreeServiceImpl implements ILearnKnowledgeTreeService
     {
         return learnKnowledgeTreeMapper.deleteLearnKnowledgeTreeById(id);
     }
+
+    /**
+     * 根据题目ID列表查询知识点树
+     * 
+     * @param questionIds 题目ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    @Override
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByQuestionIds(List<Long> questionIds)
+    {
+        return learnKnowledgeTreeMapper.selectLearnKnowledgeByQuestionIds(questionIds);
+    }
+
+    /**
+     * 根据知识点ID列表查询知识点树
+     * 
+     * @param knowledgeIds 知识点ID列表
+     * @return 知识点树集合(包含题目ID)
+     */
+    @Override
+    public List<LearnKnowledgeTree2> selectLearnKnowledgeByKnowledgeIds(List<Long> knowledgeIds)
+    {
+        return learnKnowledgeTreeMapper.selectLearnKnowledgeByKnowledgeIds(knowledgeIds);
+    }
 }

+ 8 - 0
ie-system/src/main/resources/mapper/learn/LearnKnowledgeQuestionMapper.xml

@@ -72,4 +72,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <select id="selectByQuestionIds" resultMap="LearnKnowledgeQuestionResult">
+        <include refid="selectLearnKnowledgeQuestionVo"/>
+        WHERE question_id IN
+        <foreach item="questionId" collection="questionIds" open="(" separator="," close=")">
+            #{questionId}
+        </foreach>
+    </select>
 </mapper>

+ 39 - 5
ie-system/src/main/resources/mapper/learn/LearnKnowledgeTreeMapper.xml

@@ -3,7 +3,7 @@
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper">
-    
+
     <resultMap type="LearnKnowledgeTree" id="LearnKnowledgeTreeResult">
         <result property="id"    column="id"    />
         <result property="name"    column="name"    />
@@ -17,13 +17,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="examineeTypes"    column="examineeTypes"    />
     </resultMap>
 
+    <resultMap type="LearnKnowledgeTree2" id="LearnKnowledgeTree2Result">
+        <result property="id"    column="id"    />
+        <result property="name"    column="name"    />
+        <result property="pid"    column="pid"    />
+        <result property="ancestors"    column="ancestors"    />
+        <result property="subjectId"    column="subjectId"    />
+        <result property="subjectName"    column="subjectName"    />
+        <result property="sort"    column="sort"    />
+        <result property="level"    column="level"    />
+        <result property="questionsCount"    column="questionsCount"    />
+        <result property="locations"    column="locations"    />
+        <result property="examineeTypes"    column="examineeTypes"    />
+        <result property="questionId"    column="question_id"    />
+        <result property="knowledgeQuestionId"    column="knowledge_question_id"    />
+    </resultMap>
+
     <sql id="selectLearnKnowledgeTreeVo">
         select id, name, pid, ancestors, subjectId, sort, level, questionsCount, locations, examineeTypes from learn_knowledge_tree
     </sql>
 
     <select id="selectLearnKnowledgeTreeList" parameterType="LearnKnowledgeTree" resultMap="LearnKnowledgeTreeResult">
         <include refid="selectLearnKnowledgeTreeVo"/>
-        <where>  
+        <where>
             <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
             <if test="pid != null "> and pid = #{pid}</if>
             <if test="ancestors != null  and ancestors != ''"> and ancestors = #{ancestors}</if>
@@ -35,7 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="examineeTypes != null  and examineeTypes != ''"> and (examineeTypes is null or find_in_set(#{examineeTypes}, examineeTypes))</if>
         </where>
     </select>
-    
+
     <select id="selectLearnKnowledgeTreeById" parameterType="Long" resultMap="LearnKnowledgeTreeResult">
         <include refid="selectLearnKnowledgeTreeVo"/>
         where id = #{id}
@@ -93,9 +109,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
 
     <delete id="deleteLearnKnowledgeTreeByIds" parameterType="String">
-        delete from learn_knowledge_tree where id in 
+        delete from learn_knowledge_tree where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
     </delete>
-</mapper>
+
+    <select id="selectLearnKnowledgeByQuestionIds" resultMap="LearnKnowledgeTree2Result">
+        SELECT t1.*, t2.question_id,t2.id knowledge_question_id FROM learn_knowledge_tree t1
+        INNER JOIN learn_knowledge_question t2 ON t1.id = t2.knowledge_id
+        WHERE t2.question_id IN
+        <foreach item="questionId" collection="questionIds" open="(" separator="," close=")">
+            #{questionId}
+        </foreach>
+    </select>
+
+    <select id="selectLearnKnowledgeByKnowledgeIds" resultMap="LearnKnowledgeTree2Result">
+        SELECT t1.*, t2.question_id,t2.id knowledge_question_id FROM learn_knowledge_tree t1
+        INNER JOIN learn_knowledge_question t2 ON t1.id = t2.knowledge_id
+        WHERE t2.knowledge_id IN
+        <foreach item="knowledgeId" collection="knowledgeIds" open="(" separator="," close=")">
+            #{knowledgeId}
+        </foreach>
+    </select>
+</mapper>